Mud20/accounts/
Mud20/accounts/c/
Mud20/accounts/f/
Mud20/accounts/k/
Mud20/accounts/s/
Mud20/accounts/t/
Mud20/area_current/
Mud20/area_current/newareas/
Mud20/bin/
Mud20/clans/
Mud20/gods/
Mud20/old-sources/
Mud20/player/
Mud20/player/a/del/
Mud20/player/b/
Mud20/player/b/bak/
Mud20/player/b/del/
Mud20/player/f/
Mud20/player/f/bak/
Mud20/player/f/del/
Mud20/player/k/
Mud20/player/k/bak/
Mud20/player/k/del/
Mud20/player/k/dmp/
Mud20/player/m/
Mud20/player/m/bak/
Mud20/player/o/
Mud20/player/o/bak/
Mud20/player/p/
Mud20/player/s/
Mud20/player/s/bak/
Mud20/player/s/del/
Mud20/player/t/
Mud20/player/t/del/
Mud20/player/v/
Mud20/public_html/
Mud20/races/
Mud20/skilltables/
__MACOSX/Mud20/accounts/
__MACOSX/Mud20/accounts/c/
__MACOSX/Mud20/accounts/f/
__MACOSX/Mud20/accounts/k/
__MACOSX/Mud20/accounts/s/
__MACOSX/Mud20/area_current/
__MACOSX/Mud20/area_current/core_areas/
__MACOSX/Mud20/area_current/helps/
__MACOSX/Mud20/area_current/newareas/
__MACOSX/Mud20/backups/
__MACOSX/Mud20/bin/
__MACOSX/Mud20/clans/
__MACOSX/Mud20/gods/
__MACOSX/Mud20/log/
__MACOSX/Mud20/old-sources/
__MACOSX/Mud20/player/
__MACOSX/Mud20/player/a/del/
__MACOSX/Mud20/player/b/
__MACOSX/Mud20/player/b/bak/
__MACOSX/Mud20/player/f/
__MACOSX/Mud20/player/f/bak/
__MACOSX/Mud20/player/f/del/
__MACOSX/Mud20/player/k/
__MACOSX/Mud20/player/k/bak/
__MACOSX/Mud20/player/k/del/
__MACOSX/Mud20/player/k/dmp/
__MACOSX/Mud20/player/m/
__MACOSX/Mud20/player/m/bak/
__MACOSX/Mud20/player/o/
__MACOSX/Mud20/player/o/bak/
__MACOSX/Mud20/player/p/
__MACOSX/Mud20/player/s/
__MACOSX/Mud20/player/s/bak/
__MACOSX/Mud20/player/t/del/
__MACOSX/Mud20/player/v/
__MACOSX/Mud20/public_html/
__MACOSX/Mud20/races/
__MACOSX/Mud20/skilltables/
/***************************************************************************
 * Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming   *
 * License by Wizards of the Coast. All comments referring to D20, OGL,    *
 * and SRD refer to the System Reference Document for the Open Gaming      *
 * system. Any inclusion of these derivatives must include credit to the   *
 * Mud20 system, the full and complete Open Gaming LIcense, and credit to  *
 * the respective authors. See ../doc/srd.txt for more information.        *
 *                                                                         *
 * Emud  2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem.   *
 *                                                                         *
 * MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey                *
 *                                                                         *
 * 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 St{rfeld, Tom Madsen, and Katje Nyboe.     *
 ***************************************************************************/

/***************************************************************************
 * magic.c: Spells and functions for magic															   *
 ***************************************************************************/

#include "mud.h"

/*
	Local functions.
*/
#define DO_SPELL(spell) 			bool spell( int sn, int level, CHAR_DATA *ch, void *vo, int target )
#define DO_SONG(song) 				bool song( int sn, int level, CHAR_DATA *ch, void *vo, int target )

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

/* This forces instant incubation of disease in Contagion spell */
void	disease_update		args(( CHAR_DATA *ch ));

#define turn		10
#define hr		100
#define PARTIAL -1  /* return for partial save, where TRUE is full save */

bool ForReal = TRUE;

/*
 * Hack one_argument to pick out skill names
 * This removes the need for quotes around skill
 * names in commands. This only goes three arguments deep,
 * as no skill name is longer than three words. - Kregor 7/4/12
 */
char *snarf_skill_name( char *argument, char *name )
{
	char ArgA[MAX_INPUT_LENGTH];
	char ArgB[MAX_INPUT_LENGTH];
	char ArgC[MAX_INPUT_LENGTH];

	push_call("snarf_skill_name(%p,%p)",argument,name);

	if (argument == NULL)
	{
		log_string("snarf_skill_name: argument == NULL");
		dump_stack();
		pop_call();
		return NULL;
	}

	one_argument(argument, ArgA);
	// returning empty string should stop any wierdness - Kregor
	strcpy(name, "");
	
	if (skill_lookup(ArgA) != -1)
	{
		argument = one_argument(argument, ArgA);
		strcpy(name, ArgA);
		one_argument(argument, ArgB);
		
		if (skill_lookup(format("%s %s", ArgA, ArgB)) != -1)
		{
			argument = one_argument(argument, ArgB);
			cat_sprintf(name, " %s", ArgB);
			one_argument(argument, ArgC);

			if (skill_lookup(format("%s %s", name, ArgC)) != -1)
			{
				argument = one_argument(argument, ArgC);
				cat_sprintf(name, " %s", ArgC);
			}
		}
	}
	
	pop_call();
	return argument;
}


/*
 * Called on casting of aggressive spell.
 */
void make_char_fight_char( CHAR_DATA *ch, CHAR_DATA *victim )
{
	push_call("make_char_fight_char(%p,%p)",ch,victim);

	if (ch == NULL || victim == NULL || !IS_AWAKE(victim))
	{
		pop_call();
		return;
	}

	if (ch == victim)
	{
		pop_call();
		return;
	}
	
	if (in_combat(ch) && in_combat(victim))
	{
		pop_call();
		return;
	}
	
	if (ch == supermob || victim == supermob)
	{
		pop_call();
		return;
	}

	fight(victim, ch);

	pop_call();
	return;
}

/*
 * Caster level check vs. spell resistance of vict
 * Roll 1d20 + caster level to beat spell resistance.
 * Spell penetration feat gives +1, greater penetration +2,
 * epic penetration +3 - Kregor 3/10/08
 */
bool check_spell_resist(CHAR_DATA *victim, CHAR_DATA *ch, int sn, int level)
{
	int diceroll, resist;

	push_call("check_spell_resist(%p,%p)",ch,victim);
	
	if (!is_spell_like(sn))
	{
		pop_call();
		return FALSE;
	}

	if (IS_SET(skill_table[sn].spell_school, SDESC_MIND))
	{
		if (get_curr_int(victim) < 0)
		{
			act( "$N is unaffected by your spell.", ch, NULL, victim, TO_CHAR);
			act( "You resist $n's spell.", ch, NULL, victim, TO_VICT);
			pop_call();
			return TRUE;
		}
	}
	
	if (rspec_req(victim, RSPEC_SWARM) && !IS_AREA_SPELL(sn))
	{
		act( "Your spell fails to dissipate $N.", ch, NULL, victim, TO_CHAR);
		act( "You resist $n's spell.", ch, NULL, victim, TO_VICT);
		pop_call();
		return TRUE;
	}

	if (skill_table[sn].spell_school == SCHOOL_DIVINATION)
	{
		if (domain_apotheosis(victim, DOMAIN_MADNESS) && !domain_apotheosis(ch, DOMAIN_MADNESS))
		{
			act("You fail to divine through $N's madness.", ch, NULL, victim, TO_CHAR);
			pop_call();
			return TRUE;
		}
		pop_call();
		return FALSE;
	}

	if (skill_table[sn].spell_school == SCHOOL_ILLUSION && !IS_OFFENSIVE(victim, sn))
	{
		pop_call();
		return FALSE;
	}
	
	if (IS_SET(skill_table[sn].flags, SF_NO_RESIST))
	{
		pop_call();
		return FALSE;
	}
	else if (IS_AFFECTED(victim, AFF_IMMUNE_SPELL))
	{
		act( "$N resists your spell.", ch, NULL, victim, TO_CHAR);
		act( "You resist $n's spell.", ch, NULL, victim, TO_VICT);
		pop_call();
		return TRUE;
	}
	if (sn == gsn_confusion || sn == gsn_lesser_confusion || sn == gsn_symbol_of_insanity || sn == gsn_insanity)
	{
		if (domain_apotheosis(victim, DOMAIN_MADNESS))
		{
			act("Your madness cannot surpass that already posessed by $N", ch, NULL, victim, TO_CHAR);
			pop_call();
			return TRUE;
		}
	}
	
	if ((IS_FLATFOOTED(victim) || IS_HELPLESS(victim)) && learned(ch, gsn_feint_resistance))
	{
		pop_call();
		return FALSE;
	}

	if (skill_table[sn].spell_school == SCHOOL_EVOCATION && arcane_mastery(ch, SCHOOL_EVOCATION))
		diceroll = UMAX(dice(1,20), dice(1,20));
	else
		diceroll = dice(1,20);

	diceroll += level + learned(ch, gsn_spell_penetration);

	if (race_skill(ch, gsn_high_magic))
		diceroll += 1;
	if (IS_SET(skill_table[sn].spell_desc, SDESC_NEGATIVE) && get_bloodline(ch) == BLOODLINE_UNDEAD)
		diceroll += class_level(ch, CLASS_SORCERER) / 5 + 1;
	if (learned(ch, gsn_feint_resistance))
		diceroll += UMAX(0, stat_bonus(TRUE, ch, APPLY_WIS));
		
	resist = spell_resist(victim);
	
	if (IS_EVIL(ch))
		resist = UMAX(get_apply(victim, APPLY_SR_EVIL), resist);
	if (IS_GOOD(ch))
		resist = UMAX(get_apply(victim, APPLY_SR_GOOD), resist);
	if (IS_LAWFUL(ch))
		resist = UMAX(get_apply(victim, APPLY_SR_LAW), resist);
	if (IS_CHAOTIC(ch))
		resist = UMAX(get_apply(victim, APPLY_SR_CHAOS), resist);
	
	if (!resist)
	{
		pop_call();
		return FALSE;
	}
	
	if (diceroll < resist)
	{
		act( "$N resists your spell.", ch, NULL, victim, TO_CHAR);
		act( "You resist $n's spell.", ch, NULL, victim, TO_VICT);
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}

	
/*
 * Check whether nondetection wards against a scryer
 */
bool check_nondetection ( CHAR_DATA *ch, CHAR_DATA *victim, int sn )
{
	int ranks = 0;

	push_call("check_nondetection(%p,%p)",ch,victim);
	
	if ((ranks = get_affect_level(ch, sn)) <= 0)
	{
		if ((ranks = get_caster_level(ch, sn)) <= 0)
		{
			if (sn == gsn_detect_evil && class_level(ch, CLASS_PALADIN))
			{
				ranks = class_level(ch, CLASS_PALADIN);
			}
			else if (sn == gsn_detect_good && class_level(ch, CLASS_BLACKGUARD))
			{
				ranks = class_level(ch, CLASS_BLACKGUARD);
			}
			else if ((ranks = skill_table[sn].native_level * 2) <= 0)
			{
				pop_call();
				return TRUE;
			}
		}
	}
	if (domain_apotheosis(victim, DOMAIN_MADNESS) && !domain_apotheosis(ch, DOMAIN_MADNESS))
	{
		act("You fail to divine through $N's madness.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return TRUE;
	}
	if (IS_AFFECTED(victim, AFF_MIND_BLANK))
	{
		if (!domain_apotheosis(ch, DOMAIN_KNOWLEDGE) || will_save(victim, ch, ranks, sn))
		{
			pop_call();
			return TRUE;
		}
	}
	if (IS_AFFECTED(victim, AFF_NONDETECTION))
	{
		if (dice(1,20) + ranks < 11 + UMAX(5, get_affect_level(ch, gsn_nondetection)))
		{
			pop_call();
			return TRUE;
		}
		else
		{
			pop_call();
			return TRUE;
		}
	}	
	pop_call();
	return FALSE;
}


/*
	crash-proof skill names. - Kregor
*/
char *skill_name( int sn )
{
	return (sn > -1 && is_string(skill_table[sn].name) ? skill_table[sn].name : "!unknown skill!");
}

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

	push_call("skill_lookup(%p)",name);

	for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
	{
		if (tolower(name[0]) == skill_table[sn].name[0])
		{
			if (!strcmp(name, skill_table[sn].name))
			{
				pop_call();
				return sn;
			}
		}
	}

	for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
	{
		if (tolower(name[0]) == skill_table[sn].name[0])
		{
			if (is_multi_name_short(name, skill_table[sn].name))
			{
				pop_call();
				return sn;
			}
		}
	}

	pop_call();
	return -1;
}

/*
 * is ch even a caster? - Kregor
 */
bool is_caster(CHAR_DATA *ch)
{
	int cnt;
	
	push_call("is_caster(%p)",ch);
	
	for (cnt = 0 ; cnt < MAX_CLASS ; cnt++)
	{
		if (!class_level(ch, cnt))
			continue;
		if (class_table[cnt].mana_table == MANA_NONE)
			continue;
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}


/*
 * Variation of dice for spells, to figure metamagic effects - Kregor
 */
int spell_dice( CHAR_DATA *ch, int sn, int number, int size )
{
	int idice, roll, sum, bonus;

	push_call("spell_dice(%p,%p,%p)",ch,number,size);

	if (size <= 0)
	{
		pop_call();
		return 0;
	}

	for (idice = bonus = sum = 0 ; idice < number ; idice++)
	{
		if ((roll = (lrand48() % size) + 1) == 1 && (get_bloodline(ch) == BLOODLINE_DESTINED || domain_apotheosis(ch, DOMAIN_LUCK)))
		{
			roll = (lrand48() % size) + 1;
		}
		else if (roll == 1 && is_spell(sn) && skill_table[sn].target == TAR_CHAR_OFFENSIVE && domain_apotheosis(ch, DOMAIN_DESTRUCTION))
		{
			roll = 2;
		}
		sum += roll;
	}
	
	if (IS_SET(ch->cast_feats, METAMAGIC_EMPOWER)
	|| (ch->cast_class == CLASS_CLERIC && strstr(skill_table[sn].name, "inflict") && learned(ch, gsn_empower_infliction))
	|| (ch->cast_class == CLASS_CLERIC && strstr(skill_table[sn].name, "cure") && learned(ch, gsn_empower_curing)))
		bonus = sum / 2;

	if (IS_SET(ch->cast_feats, METAMAGIC_MAXIMIZE)
	|| (ch->cast_class == CLASS_CLERIC && strstr(skill_table[sn].name, "cure") && domain_apotheosis(ch, DOMAIN_HEALING)))
		sum = number * size;

	pop_call();
	return sum + bonus;
}


/*
 * Return the casting class of a spell - Kregor
 * If cast as a racial ability, return -2.
 * If not castable by any class CH has, return -1.
 */
int casting_class( CHAR_DATA *ch, int sn, bool fCasting )
{
	int class = -1;

	push_call("casting_class(%p,%p)",ch,sn);
	
	if (ch->casting && ch->cast_sn == sn)
	{
		class = ch->cast_class;
	}
	else if (!fCasting)
	{
		if ((class = prepared(ch,sn)) == -1)
		{
			if ((class = spontaneous_cast(ch,sn)) == -1)
			{
				if ((class = multi(ch,sn)) == -1)
				{
					if (race_skill(ch, sn))
					{
						class = -2;
					}
				}
			}
		}
	}
	pop_call();
	return class;
}


/*
 * Return the class a given spell is
 * prepared as - Kregor
 */
int prepared( CHAR_DATA *ch, int sn )
{
	int class = -1;

	push_call("prepared(%p,%p)",ch,sn);
	
	if (IS_NPC(ch) || IS_GOD(ch))
	{
		class = multi(ch, sn);
	}
	else if (ch->pcdata->prepared[sn] > 0)
		class = ch->pcdata->prepared[sn];
		
	pop_call();
	return class;
}


/*
 * Calc the effective caster level of a class,
 * including prestige classes that add caster levels - Kregor
 *
 * per d20, prestige classes add to spells known,
 * spells per day (mana), and caster level, so this
 * function should find be in functions for all three.
 */
int eff_caster_level( CHAR_DATA *ch, int class )
{
	int cls, lvl, level;
	
	push_call("eff_caster_level(%p.%p)",ch,class);
	
	if ((lvl = UMIN(ch->mclass[class], 20)) < 1)
	{
		pop_call();
		return 0;
	}
	
	/*
	 * add caster levels for prestige classes
	 * Order of progression: Wizard or Sorcerer over Bard for Arcane,
	 * Cleric or Druid over Paladin or Ranger for Divine,
	 * Cleric or Druid over Wizard or Sorcerer for Either
	 * Because the code does not allow Cleric/Druid or Wizard/Sorcerer multi,
	 * no safeguards are in place for having levels in both.
	 */
	for (cls = MAX_CORE_CLASS ; cls < MAX_PRESTIGE_CLASS ; cls++)
	{
		if ((level = class_level(ch, cls)) <= 0)
			continue;
		if (cls == class)
			continue;
		switch (class_table[cls].mana_table)
		{
			default:
				break;
			case MANA_PRESTIGE_ARC:
			case MANA_PREST_ARC_HALF:
				if (class_table[cls].mana_table == MANA_PREST_ARC_HALF)
					level = ROUNDUP(level / 2);
				if (class == CLASS_WIZARD)
					lvl += level;
				else if (class == CLASS_SORCERER)
					lvl += level;
				else if (class == CLASS_BARD && !class_level(ch, CLASS_WIZARD) && !class_level(ch, CLASS_SORCERER))
					lvl += level;
				break;
			case MANA_PRESTIGE_DIV:
			case MANA_PREST_DIV_HALF:
				if (class_table[cls].mana_table == MANA_PREST_DIV_HALF)
					level = ROUNDUP(level / 2);
				if (class == CLASS_CLERIC)
					lvl += level;
				else if (class == CLASS_DRUID)
					lvl += level;
				else if (class == CLASS_PALADIN && !class_level(ch, CLASS_CLERIC) && !class_level(ch, CLASS_DRUID))
					lvl += level;
				else if (class == CLASS_RANGER && !class_level(ch, CLASS_CLERIC) && !class_level(ch, CLASS_PALADIN) && !class_level(ch, CLASS_CLERIC) && !class_level(ch, CLASS_DRUID))
					lvl += level;
				break;
			case MANA_PRESTIGE_EITHER:
				if (class == CLASS_CLERIC)
					lvl += level;
				else if (class == CLASS_DRUID)
					lvl += level;
				else if (class == CLASS_WIZARD && !class_level(ch, CLASS_CLERIC) && !class_level(ch, CLASS_DRUID))
					lvl += level;
				else if (class == CLASS_SORCERER && !class_level(ch, CLASS_CLERIC) && !class_level(ch, CLASS_PALADIN) && !class_level(ch, CLASS_CLERIC) && !class_level(ch, CLASS_DRUID))
					lvl += level;
				break;
			case MANA_PRESTIGE_BOTH:
				if (class == CLASS_CLERIC)
					lvl += level;
				else if (class == CLASS_DRUID)
					lvl += level;
				if (class == CLASS_WIZARD)
					lvl += level;
				else if (class == CLASS_SORCERER)
					lvl += level;
				break;
		}
		break; //because code only allows one prestige class for PC, break after finding a prestige class
	}

	lvl -= get_apply(ch, APPLY_LEVEL); // don't forget penalty for level drain!
	
	pop_call();
	return UMAX(1,lvl); // Min caster level even with level drains is 1st
}


/*
 * returns the max spell circle castable 
 * by a PC's class level - Kregor 5/25/07
 */
int max_spell_circle( CHAR_DATA *ch, int class )
{
	int circle, lvl;
	
	push_call("max_spell_circle(%p.%p)",ch,class);
	
	if ((lvl = UMIN(eff_caster_level(ch, class), 30)) < 1)
	{
		pop_call();
		return -1;
	}
	
	switch (class_table[class].mana_table)
	{
		default:
			circle = -1;
			break;
		case MANA_SORCERER:
			circle = URANGE( 1, lvl / 2, 9);
			break;
		case MANA_WIZ_PRIEST:
			circle = ROUNDUP((lvl - 1) / 2);
			circle = UMIN(circle, 9);
			break;
		case MANA_BARD:
			circle = URANGE(0, lvl / 2, 6);
			break;
		case MANA_PRESTIGE:
			if (class == CLASS_BLACKGUARD)
				lvl += class_level(ch,CLASS_PALADIN);
			circle = UMIN((lvl + 3) / 3, 4);
			break;
		case MANA_WARRIOR:
			circle = UMIN((lvl - 1) / 3, 4);
			break;
// 		case MANA_PRESTIGE:
// 			circle = UMIN(lvl / 2, 4);
// 			break;
	}
	
	pop_call();
	return UMIN(circle, 9);
}


/*
 * The number of a given level of a spell character may prepare
 * Simple, count the number of each slot at the caster level,
 * and add the number of spell points needed to cast that many
 * of a given slot number - Kregor
 */
int get_slot_count( CHAR_DATA *ch, int class, int level )
{
	int count = 0;
	int lvl = 0;
	int cnt = 0;
	
	push_call("get_slot_count(%p,%p,%p)",ch,class,level);
	
	if (level < 0 || level > 9)
	{
		log_printf("get_slot_count: spell level out of range!");
		pop_call();
		return 0;
	}
	if ((lvl = eff_caster_level(ch, class)) <= 0)
	{
		pop_call();
		return 0;
	}
	
	switch (class_table[class].mana_table)
	{
		default:
			count = -1;
			break;
		case MANA_BARD:
			count = bard_spell_table[lvl].spell_level[level];
			break;
		case MANA_WIZ_PRIEST:
			count = spell_slot_table[lvl].spell_level[level];
			break;
		case MANA_PRESTIGE:
			if (class == CLASS_BLACKGUARD)
				lvl += class_level(ch,CLASS_PALADIN);
			count = warrior_spell_table[lvl+5].spell_level[level];
			break;
		case MANA_WARRIOR:
			count = warrior_spell_table[lvl].spell_level[level];
			break;
		case MANA_SORCERER:
			count = sorcerer_spell_table[lvl].spell_level[level];
			break;
	}
	
	if (count < 0)
	{
		pop_call();
		return count;
	}
	
	switch (class_table[class].attr_prime)
	{
		default:
			break;
		case APPLY_WIS:
			count += slot_bonus_table[ch->perm_wis].bonus[level];
			break;
		case APPLY_INT:
			count += slot_bonus_table[ch->perm_int].bonus[level];
			break;
		case APPLY_CHA:
			count += slot_bonus_table[ch->perm_cha].bonus[level];
			break;
	}

// 	if (class == CLASS_CLERIC && ch->pcdata->domains > 0)
// 		count += 1;
// 
	if (class == CLASS_WIZARD && get_school(ch) > 0)
		count += 1;

	if ((cnt = learned(ch, gsn_newfound_arcana)) > 0)
	{
		if (cnt >= 1 && level == 1)
			count++;
		if (cnt >= 2 && level == 2)
			count++;
		if (cnt >= 3 && level == 3)
			count++;
	}
	pop_call();
	return count;
}

/* 
 * Uses above functions to calculate the mana
 * gained for a given class level - Kregor
 */
int get_max_mana( CHAR_DATA *ch, int class )
{
	int mana, lev;

	push_call("get_max_mana(%p)",ch);

	if (!class_level(ch, class))
	{
		pop_call();
		return 0;
	}
	if (class_table[class].mana_table == MANA_NONE)
	{
		pop_call();
		return 0;
	}

	for (mana = lev = 0 ; lev <= max_spell_circle(ch, class) ; lev++)
	{
		mana += get_slot_count(ch, class, lev) * UMAX(0, lev * 2 - 1);
	}

	if (mana > 0)
	{
		mana += ch->apply[APPLY_MANA];
	}

	pop_call();
	return(UMAX(0,mana));
}

/*
 * The number of a given level of a spell
 * spontaneous caster may learn - Kregor
 */
int can_learn_spells( CHAR_DATA *ch, int class, int level )
{
	int count = 0;
	int lvl = 0;
	
	push_call("can_learn_spells(%p,%p,%p)",ch,class,level);
	
	if (level < 0 || level > 9)
	{
		log_printf("can_learn_spells: spell level out of range!");
		pop_call();
		return 0;
	}	
	if ((lvl = eff_caster_level(ch, class)) <= 0)
	{
		pop_call();
		return 0;
	}
	if (class == CLASS_BARD && level > 6)
	{
		pop_call();
		return 0;
	}
	
	switch (class_table[class].mana_table)
	{
		case MANA_BARD:
			count = bard_known_table[lvl].spell_level[level];
			break;
		case CLASS_SORCERER:
			count = sorcerer_known_table[lvl].spell_level[level];
			break;
		default:
			count = 0;
	}
		
	pop_call();
	return count;
}

/*
 * Returns TRUE if the number above is met
 * clears extra spells from preparation if
 * it was exceeded. - Kregor
 */
int slots_full( CHAR_DATA *ch, int class, int level )
{
	int sn, count;
	
	push_call("slots_full(%p,%p,%p)",ch,class,level);
	
	for (count = sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
	{
		if (!is_spell(sn))
			continue;
			
		if (prepared(ch, sn) == class && circle_by_class(ch, sn, class) == level)
			count++;

		if (count > get_slot_count(ch, class, level))
		{
			ch->pcdata->prepared[sn] = 0;
			count--;
		}
	}
	pop_call();
	return count;
}


/*
 * Returns number of spells learned of a given level
 * for spontaneous casters - Kregor
 */
int slots_learned( CHAR_DATA *ch, int class, int level )
{
	int sn, count, classes;
	
	push_call("slots_learned(%p,%p,%p)",ch,class,level);
	
	for (count = sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
	{
		if (!is_spell(sn))
			continue;
			
		if ((classes = learned(ch, sn)) <= 0)
			continue;
			
		if (!IS_SET(classes, 1 << class))
			continue;
			
		//don't count bloodline bonus spell in number learned
		if (class == CLASS_SORCERER && skill_table[sn].bloodline[get_bloodline(ch)] == level)
			continue;

		if (skill_table[sn].skill_level[class] != level)
			continue;
			
		count++;
	}
	pop_call();
	return count;
}


/*
 * return TRUE if onlooker can see the spell being cast - Kregor
 */
bool can_see_casting(CHAR_DATA *ch, CHAR_DATA *caster, int sn)
{
	int feats = caster->metamagic;
	
	push_call("can_see_casting(%p,%p,%p)",ch,caster,sn);

	if (!caster->casting)
	{
		pop_call();
		return FALSE;
	}

	if (IS_SET(feats, METAMAGIC_DISGUISE))
	{
		pop_call();
		return FALSE;
	}
	
	if (ch == NULL)
	{
		pop_call();
		return FALSE;
	}
	
	if (IS_SET(skill_table[sn].flags, SF_VERBAL))
	{
		if (!IS_SET(feats, METAMAGIC_SILENT) && can_hear(ch, caster))
		{
			pop_call();
			return TRUE;
		}
	}
	if (IS_SET(skill_table[sn].flags, SF_SOMATIC))
	{
		if (!IS_SET(feats, METAMAGIC_STILL) && can_see(ch, caster))
		{
			pop_call();
			return TRUE;
		}
	}
	pop_call();
	return FALSE;
}
	

/*
 *	Returns -1 if one cannot cast the spell, otherwise it returns the
 *	caster level. Modified to allow mobs with race or classes set
 *	cast based on their race and class specs, otherwise, generic
 *	mobs cast any spell at their level - Kregor 04/13/07
 */
int get_caster_level( CHAR_DATA *ch, int sn )
{
	int level = -1;
	int class;

	push_call("get_caster_level(%p,%p)",ch,sn);

	if (!is_spell(sn))
	{
		log_build_printf(ch->pIndexData->vnum, "get_caster_level: %s: not a spell", skill_table[sn].name);
		pop_call();
		return -1;
	}

	// Don't bother with all the below if it's already being cast.
	if (ch->casting && ch->cast_sn == sn && ch->cast_level != -1)
	{
		pop_call();
		return ch->cast_level;
	}
	
	if (IS_NPC(ch))
	{
		if (IS_CLASSED(ch))
		{
			if ((level = multi_caster_level(ch, sn)) != -1)
			{
				pop_call();
				return(level);
			}
		}
		if (get_race(ch) > RACE_NONE)
		{
			if (race_skill(ch, sn))
			{
				level = ch->level;
				pop_call();
				return(level);
			}
			else
			{
				log_build_printf(ch->pIndexData->vnum, "get_caster_level: %s: not valid spell for race or class", skill_table[sn].name);
				pop_call();
				return(-1);
			}
		}
		else
		{
			pop_call();
			return(UMIN(ch->level, 30));
		}
		pop_call();
		return(level);
	}

	/* prevent clerics from casting opposing spells regardless of multiclass */
	if (!IS_NPC(ch) && class_level(ch, CLASS_CLERIC))
	{
		if (opposing_domain(ch, sn))
		{
			send_to_char( "Your ethos prevents you from casting that spell.\n\r", ch );
			pop_call();
			return -1;
		}
	}

	/* if not prepared, we fall back to spontaneous casting */
	if ((class = prepared(ch, sn)) == -1)
	{
		if ((class = spontaneous_cast(ch, sn)) == -1)
		{
			if (!race_skill(ch, sn))
			{
				send_to_char( "You have not prepared that spell.\n\r", ch);
				pop_call();
				return -1;
			}
			else
			{
				pop_call();
				return ch->level;
			}
		}
	}

	level = eff_caster_level(ch, class); //adds prestige classes that add caster levels	
	//caster level for Pally/Rgr is half actual level - Kregor
	if (class_table[class].mana_table == MANA_WARRIOR)
	{
		level -= class_level(ch, class) / 2;
	}
	
	wiz_printf("Actual caster level: %d", level);

	// Mystic Theurge Shared Circle ability - Kregor
	if (class_level(ch, CLASS_MYSTIC_THEURGE))
	{
		if (circle_by_class(ch, sn, class) <= ROUNDUP(class_level(ch, CLASS_MYSTIC_THEURGE) / 2))
		{
			if (class == CLASS_CLERIC || class == CLASS_DRUID)
			{
				level += class_level(ch, CLASS_WIZARD) + class_level(ch, CLASS_SORCERER);
			}
			else if (class == CLASS_WIZARD || class == CLASS_SORCERER)
			{
				level += class_level(ch, CLASS_CLERIC) + class_level(ch, CLASS_DRUID);
			}
		}
	}
	
	if (class == CLASS_WIZARD && school_spell(ch, sn)) // specialist wizards cast school spells at +1 caster level
		level++;

 	if (class == CLASS_SORCERER) // bloodline abilities which give caster level bonus
	{
		if (IS_SET(skill_table[sn].spell_desc, SDESC_AIR) && get_bloodline(ch) == BLOODLINE_AIR)
			level++;
		if (IS_SET(skill_table[sn].spell_desc, SDESC_EARTH) && get_bloodline(ch) == BLOODLINE_EARTH)
			level++;
		if (IS_SET(skill_table[sn].spell_desc, SDESC_FIRE) && get_bloodline(ch) == BLOODLINE_FIRE)
			level++;
		if (IS_SET(skill_table[sn].spell_desc, SDESC_WATER) && get_bloodline(ch) == BLOODLINE_WATER)
			level++;
		if (race_skill(ch, gsn_elemental_affinity))
		{
			if (ch->race == RACE_PLANETOUCHED_AIR && get_bloodline(ch) == BLOODLINE_AIR
			&& skill_table[sn].bloodline[BLOODLINE_AIR] <= max_spell_circle(ch, CLASS_SORCERER))
			{
				level++;
			}
			if (ch->race == RACE_PLANETOUCHED_FIRE && get_bloodline(ch) == BLOODLINE_FIRE
			&& skill_table[sn].bloodline[BLOODLINE_FIRE] <= max_spell_circle(ch, CLASS_SORCERER))
			{
				level++;
			}
			if (ch->race == RACE_PLANETOUCHED_EARTH && get_bloodline(ch) == BLOODLINE_EARTH
			&& skill_table[sn].bloodline[BLOODLINE_EARTH] <= max_spell_circle(ch, CLASS_SORCERER))
			{
				level++;
			}
			if (ch->race == RACE_PLANETOUCHED_WATER && get_bloodline(ch) == BLOODLINE_WATER
			&& skill_table[sn].bloodline[BLOODLINE_WATER] <= max_spell_circle(ch, CLASS_SORCERER))
			{
				level++;
			}
		}
		else if (race_skill(ch, gsn_fiendish_affinity))
		{
			if (get_bloodline(ch) == BLOODLINE_ABYSSAL
			&& skill_table[sn].bloodline[BLOODLINE_ABYSSAL] <= max_spell_circle(ch, CLASS_SORCERER))
			{
				level++;
			}
			else if (get_bloodline(ch) == BLOODLINE_INFERNAL
			&& skill_table[sn].bloodline[BLOODLINE_INFERNAL] <= max_spell_circle(ch, CLASS_SORCERER))
			{
				level++;
			}
		}
		if (race_skill(ch, gsn_celestial_affinity) && get_bloodline(ch) == BLOODLINE_CELESTIAL
		&& skill_table[sn].bloodline[BLOODLINE_CELESTIAL] <= max_spell_circle(ch, CLASS_SORCERER))
		{
			level++;
		}
		if (race_skill(ch, gsn_shadow_affinity) && IS_SET(skill_table[sn].spell_desc, SDESC_DARKNESS))
		{
			level++;
		}
	}

 	if (class == CLASS_CLERIC) // domain benefits which give caster level bonus
	{
		if (has_domain(ch, DOMAIN_KNOWLEDGE) && IS_SET(skill_table[sn].spell_school, SCHOOL_DIVINATION))
			level++;
		if (has_domain(ch, DOMAIN_GOOD) && IS_SET(skill_table[sn].spell_desc, SDESC_GOOD))
			level++;
		if (has_domain(ch, DOMAIN_CHAOS) && IS_SET(skill_table[sn].spell_desc, SDESC_CHAOTIC))
			level++;
		if (has_domain(ch, DOMAIN_EVIL) && IS_SET(skill_table[sn].spell_desc, SDESC_EVIL))
			level++;
		if (has_domain(ch, DOMAIN_LAW) && IS_SET(skill_table[sn].spell_desc, SDESC_LAWFUL))
			level++;
			
		if (race_skill(ch, gsn_elemental_affinity))
		{
			if (ch->race == RACE_PLANETOUCHED_AIR && has_domain(ch, DOMAIN_AIR)
			&& skill_table[sn].domains[DOMAIN_AIR] <= max_spell_circle(ch, CLASS_CLERIC))
			{
				level++;
			}
			if (ch->race == RACE_PLANETOUCHED_FIRE && has_domain(ch, DOMAIN_FIRE)
			&& skill_table[sn].domains[DOMAIN_FIRE] <= max_spell_circle(ch, CLASS_CLERIC))
			{
				level++;
			}
			if (ch->race == RACE_PLANETOUCHED_EARTH && has_domain(ch, DOMAIN_EARTH)
			&& skill_table[sn].domains[DOMAIN_EARTH] <= max_spell_circle(ch, CLASS_CLERIC))
			{
				level++;
			}
			if (ch->race == RACE_PLANETOUCHED_WATER && has_domain(ch, DOMAIN_WATER)
			&& skill_table[sn].domains[DOMAIN_WATER] <= max_spell_circle(ch, CLASS_CLERIC))
			{
				level++;
			}
		}
		if (race_skill(ch, gsn_fiendish_affinity) && has_domain(ch, DOMAIN_EVIL)
		&& skill_table[sn].domains[DOMAIN_EVIL] <= max_spell_circle(ch, CLASS_CLERIC))
		{
			level++;
		}
		if (race_skill(ch, gsn_celestial_affinity) && has_domain(ch, DOMAIN_GOOD)
		&& skill_table[sn].domains[DOMAIN_GOOD] <= max_spell_circle(ch, CLASS_CLERIC))
		{
			level++;
		}
		if (race_skill(ch, gsn_shadow_affinity) && has_domain(ch, DOMAIN_DARKNESS)
		&& skill_table[sn].domains[DOMAIN_DARKNESS] <= max_spell_circle(ch, CLASS_CLERIC))
		{
			level++;
		}
	}
	wiz_printf("Effective caster level: %d", level);
	
	pop_call();
	return(level);
}


/*
 * returns the class CH can cast a spell spontaneously 
 * grabbing the first class that has mana to cast it,
 * or -1 if cannot be spontaenously cast by CH in any class - Kregor
 */
int spontaneous_cast( CHAR_DATA *ch, int sn )
{
	int circle, class, skill, cnt;
	
	push_call("spontaneous_cast(%p,%p)",ch,sn);
	
	circle = class = -1;
	
	if (!is_spell(sn))
	{
		pop_call();
		return -1;
	}
	
	if ((skill = learned(ch, sn)) <= 0)
	{
		pop_call();
		return -1;
	}
		
	// spontaneous domain casting, instead of spontaneous cure
	if (class_level(ch, CLASS_CLERIC) && IS_SHIFT(skill, CLASS_CLERIC))
	{
		for (cnt = 0 ; cnt < MAX_DOMAIN ; cnt++)
		{
			if (has_domain(ch, cnt))
			{
				if ((circle = skill_table[sn].domains[cnt]) <= max_spell_circle(ch, CLASS_CLERIC))
				{
					if (ch->mana[CLASS_CLERIC] >= get_mana(ch, sn, skill_table[sn].domains[cnt], CLASS_CLERIC))
					{
						class = CLASS_CLERIC;
					}
				}
			}
		}
	}
	
	// druids spontaneous Nature's Ally spells
	if (class == -1)
	{
		if (class_level(ch, CLASS_DRUID) && IS_SHIFT(skill, CLASS_DRUID))
		{	
			if (!str_prefix(skill_table[sn].name, "natures ally"))
			{
				if ((circle = circle_by_class(ch, sn, CLASS_DRUID)) != -1)
				{
					if (ch->mana[CLASS_DRUID] >= get_mana(ch, sn, circle, CLASS_DRUID))
					{
						class = CLASS_DRUID;
					}
				}
			}
		}
	}
	if (class == -1)
	{
		if (class_level(ch, CLASS_SORCERER) && IS_SHIFT(skill, CLASS_SORCERER))
		{	
			if ((circle = circle_by_class(ch, sn, CLASS_SORCERER)) != -1)
			{
				if (ch->mana[CLASS_SORCERER] >= get_mana(ch, sn, circle, CLASS_SORCERER))
				{
					class = CLASS_SORCERER;
				}
			}
		}
	}
	if (class == -1)
	{
		if (class_level(ch, CLASS_BARD) && IS_SHIFT(skill, CLASS_BARD))
		{	
			if ((circle = circle_by_class(ch, sn, CLASS_BARD)) != -1)
			{
				if (ch->mana[CLASS_BARD] >= get_mana(ch, sn, circle, CLASS_BARD))
				{
					class = CLASS_BARD;
				}
			}
		}
	}	
	pop_call();
	return class;
}
		

/*
 * Revised to return only the highest caster level
 * that could cast the spell - Kregor 4/13/07
 */
int multi_caster_level( CHAR_DATA *ch, int sn)
{
	int cnt, mlv;

	push_call("multi_caster_level(%p,%p)",ch,sn);

	mlv = -1;

	for (cnt = 0 ; cnt < MAX_CLASS ; cnt++)
	{
		if (!class_level(ch, cnt))
			continue;
		if (skill_table[sn].skill_level[cnt] < LEVEL_IMMORTAL && max_spell_circle(ch, cnt) >= skill_table[sn].skill_level[cnt])
		{
			mlv = UMAX(class_level(ch, cnt), mlv);
		}
	}
	pop_call();
	return(mlv);
}

/*
 * Returns -1 if given class cannot cast a spell
 * at the character's given class level, otherwise
 * returns circle of spell. Assumes check already made
 * for whether spell is known. - Kregor 10/9/07
 */
int circle_by_class( CHAR_DATA *ch, int sn, int class )
{
	int level, cnt;

	push_call("circle_by_class(%p,%p,%p)",ch,sn,class);

	level = -1;
	
	if (class == -1)
	{
		pop_call();
		return(-1);
	}

	if (class == CLASS_CLERIC)
	{
		if (IS_GOOD(ch) && IS_SET(skill_table[sn].spell_desc, SDESC_EVIL) && !IS_SET(skill_table[sn].spell_desc, SDESC_GOOD))
		{
			pop_call();
			return -1;
		}
		if (IS_EVIL(ch) && IS_SET(skill_table[sn].spell_desc, SDESC_GOOD) && !IS_SET(skill_table[sn].spell_desc, SDESC_EVIL))
		{
			pop_call();
			return -1;
		}
		if (IS_CHAOTIC(ch) && IS_SET(skill_table[sn].spell_desc, SDESC_LAWFUL) && !IS_SET(skill_table[sn].spell_desc, SDESC_CHAOTIC))
		{
			pop_call();
			return -1;
		}
		if (IS_LAWFUL(ch) && IS_SET(skill_table[sn].spell_desc, SDESC_CHAOTIC) && !IS_SET(skill_table[sn].spell_desc, SDESC_LAWFUL))
		{
			pop_call();
			return -1;
		}
		for (cnt = 0 ; cnt < MAX_DOMAIN ; cnt++)
		{
			if (has_domain(ch, cnt))
			{
				if (max_spell_circle(ch, class) >= skill_table[sn].domains[cnt])
				{
					level = skill_table[sn].domains[cnt];
					break;
				}
			}
		}
	}

	if (level == -1 && max_spell_circle(ch, class) >= skill_table[sn].skill_level[class])
	{
		level = skill_table[sn].skill_level[class];
	}
		
	pop_call();
	return(level);
}


/*
 * Revised to return the highest spell circle if NPC,
 * or the circle for the class prepared in if PC
 * or the native spell circle if caster is NULL - Kregor 10/9/07
 * Added reading casting data to get circle from cast spell - Kregor 11/6/10
 */
int get_spell_circle( CHAR_DATA *ch, int sn)
{
	int class, mlv;

	push_call("get_spell_circle(%p,%p)",ch,sn);

	mlv = -1;

	if (ch == NULL)
	{
		pop_call();
		return skill_table[sn].native_level;
	}	
	
	// logically, if casting this spell, then the spell circle is already known.
	// if cast_circle is < 0, then it's an obj or race cast, which uses native circle.
	if (ch->casting && ch->cast_sn == sn)
	{
		if (ch->cast_circle >= 0)
		{
			pop_call();
			return (ch->cast_circle);
		}
		else
		{
			pop_call();
			return (skill_table[sn].native_level);
		}
	}

	if (IS_NPC(ch))
	{
		if (IS_CLASSED(ch))
		{
			if ((class = multi(ch, sn)) <= 0)
			{
				pop_call();
				return -1;
			}
			mlv = skill_table[sn].skill_level[class];
		}
		else
		{
			mlv = skill_table[sn].native_level;
		}
	}
	else
	{
		if ((class = prepared(ch, sn)) == -1)
		{
			if ((class = spontaneous_cast(ch, sn)) == -1)
			{
				if (!race_skill(ch, sn))
				{
					mlv = -1;
				}
				else
				{
					mlv = skill_table[sn].native_level;
				}
			}
		}
		if (class != -1)
		{
			mlv = circle_by_class(ch, sn, class);
		}
	}
		
	if (ch != NULL)
		wiz_printf("{058}get_spell_circle: %s circle %s", numbersuf(mlv), class > 0 ? class_types[class] : "");

	pop_call();
	return(mlv);
}


/*
 * Return caster level of obj - Kregor 4/13/07
 * Blank or insuffient value on objects
 * is overridden by minimum level to cast.
 */
int item_caster_level( OBJ_DATA *obj, int sn )
{
	int mlv;

	push_call("item_caster_level(%p,%p)",obj,sn);

	if (sn == -1 || !is_spell(sn))
	{
		pop_call();
		return -1;
	}

	mlv = (get_spell_circle(NULL, sn) * 2) - 1;

	if (IS_OBJ_TYPE(obj, ITEM_STAFF)
	|| IS_OBJ_TYPE(obj, ITEM_WAND)
	|| IS_OBJ_TYPE(obj, ITEM_POTION)
	|| IS_OBJ_TYPE(obj, ITEM_PILL))
	{
		if (obj->value[0] > mlv)
			mlv = obj->value[0];
	}
	pop_call();
	return(mlv);
}

/*
 * checks to see if specialist wizard has
 * prepared a school spell in a given level
 */
bool prepared_school( CHAR_DATA *ch, int level )
{
	int sn;

	push_call("prepared_school(%p,%p)",ch,level);

	if (IS_NPC(ch) || !class_level(ch, CLASS_WIZARD))
	{
		pop_call();
		return FALSE;
	}
	
	for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
	{
		if (!is_spell(sn))
			continue;
		
		if (skill_table[sn].skill_level[CLASS_WIZARD] != level)
			continue;
			
		if (skill_table[sn].spell_school != get_school(ch))
			continue;

		if (prepared(ch, sn) == CLASS_WIZARD)
		{
			pop_call();
			return TRUE;
		}
	}
	
	pop_call();
	return FALSE;
}


// /*
//  * checks to see if a cleric has
//  * prepared a domain spell in a given level
//  */
// bool prepared_domain( CHAR_DATA *ch, int level )
// {
// 	int sn, dom;
// 
// 	push_call("prepared_school(%p,%p)",ch,level);
// 
// 	if (IS_NPC(ch) || !class_level(ch, CLASS_CLERIC))
// 	{
// 		pop_call();
// 		return FALSE;
// 	}
// 	
// 	for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
// 	{
// 		if (!is_spell(sn))
// 			continue;
// 		
// 		for (dom = 0 ; dom < MAX_DOMAIN ; dom++)
// 		{
// 			if (!ch->pcdata->domain[dom])
// 				continue;
// 
// 			if (skill_table[sn].domains[dom] != level)
// 				continue;
// 
// 			if (prepared(ch, sn) == CLASS_CLERIC)
// 			{
// 				pop_call();
// 				return TRUE;
// 			}
// 		}
// 	}
// 	
// 	pop_call();
// 	return FALSE;
// }
// 

/*
 * Answer me TRUE if you are a domain spell at X level
 */
bool domain_spell( CHAR_DATA *ch, int sn, int level )
{
	int dom;

	push_call("domain_spell(%p,%p,%p)",ch,sn,level);

	if (!class_level(ch, CLASS_CLERIC))
	{
		pop_call();
		return FALSE;
	}
	for (dom = 0 ; dom < MAX_DOMAIN ; dom++)
	{
		if (!has_domain(ch, dom))
			continue;

		if (skill_table[sn].domains[dom] != level)
			continue;

		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}


/*
 * Answer me TRUE if a spell is opposite cleric's alignment
 */
bool opposing_domain( CHAR_DATA *ch, int sn )
{
	push_call("opposing_domain(%p,%p)",ch,sn);

	if (!class_level(ch, CLASS_CLERIC))
	{
		pop_call();
		return FALSE;
	}
	if (IS_GOOD(ch) && IS_SET(skill_table[sn].spell_desc, SDESC_EVIL) && !IS_SET(skill_table[sn].spell_desc, SDESC_GOOD))
	{
		pop_call();
		return TRUE;
	}
	if (IS_EVIL(ch) && IS_SET(skill_table[sn].spell_desc, SDESC_GOOD) && !IS_SET(skill_table[sn].spell_desc, SDESC_EVIL))
	{
		pop_call();
		return TRUE;
	}
	if (IS_CHAOTIC(ch) && IS_SET(skill_table[sn].spell_desc, SDESC_LAWFUL) && !IS_SET(skill_table[sn].spell_desc, SDESC_CHAOTIC))
	{
		pop_call();
		return TRUE;
	}
	if (IS_LAWFUL(ch) && IS_SET(skill_table[sn].spell_desc, SDESC_CHAOTIC) && !IS_SET(skill_table[sn].spell_desc, SDESC_LAWFUL))
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}


/*
 * Caved in and went the way of NWN and 2E, made opposing schools
 * automatic and fixed. Helps prevent school optimizing anyway - Kregor
 */
int opp_school( CHAR_DATA *ch )
{
	int school;

	push_call("opp_school(%p)",ch);

	if (!class_level(ch, CLASS_WIZARD))
	{
		pop_call();
		return -1;
	}
	
	switch (get_school(ch))
	{
		default:
			school = -1;
			break;
		case SCHOOL_ABJURATION:
			school = SCHOOL_TRANSMUTATION;
			break;
		case SCHOOL_TRANSMUTATION:
			school = SCHOOL_ABJURATION;
			break;
		case SCHOOL_CONJURATION:
			school = SCHOOL_NECROMANCY;
			break;
		case SCHOOL_DIVINATION:
			school = SCHOOL_ILLUSION;
			break;
		case SCHOOL_ENCHANTMENT:
			school = SCHOOL_EVOCATION;
			break;
		case SCHOOL_EVOCATION:
			school = SCHOOL_ENCHANTMENT;
			break;
		case SCHOOL_ILLUSION:
			school = SCHOOL_DIVINATION;
			break;
		case SCHOOL_NECROMANCY:
			school = SCHOOL_CONJURATION;
			break;
	}
	pop_call();
	return school;
}
			
/*
 * Answer me TRUE if a spell is in PC's specialty school - Kregor
 */
bool school_spell( CHAR_DATA *ch, int sn )
{
	int school;

	push_call("school_spell(%p,%p)",ch,sn);

	if (IS_NPC(ch) || !class_level(ch, CLASS_WIZARD))
	{
		pop_call();
		return FALSE;
	}
	if ((school = get_school(ch)) <= 0)
	{
		pop_call();
		return FALSE;
	}
	if (school == skill_table[sn].spell_school)
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}


/*
 * Answer me TRUE if a spell is in PC's opposition schools - Kregor
 */
bool opposing_school( CHAR_DATA *ch, int sn )
{
	int school;

	push_call("opposing_school(%p,%p)",ch,sn);

	if (IS_NPC(ch) || !class_level(ch, CLASS_WIZARD))
	{
		pop_call();
		return FALSE;
	}
	if ((school = opp_school(ch)) == 0)
	{
		pop_call();
		return FALSE;
	}
	if (school == skill_table[sn].spell_school)
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}


/*
 * Is a spell known in a given class?
 * Support for d20 spell learning, because you
 * can learn a spell that overlaps classes
 * but only know one of the types. Solution
 * is to set the bitvector of the class, since
 * it will be base classes only - Kregor
 */
bool learned_in_class(CHAR_DATA *ch, int sn, int class)
{
	push_call("learned_in_class(%p,%p,%p)",ch,sn,class);
	
	if (IS_NPC(ch))
	{
		pop_call();
		return FALSE;
	}
	if (IS_SET(learned(ch, sn), 1 << class))
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}

/*
 * Is a spell scribed into given spellbook?
 * Support for d20 spell learning - Kregor
 */
bool spell_in_book(OBJ_DATA *obj, int sn)
{
	push_call("spell_in_book(%p,%p)",obj,sn);
	
	if (obj == NULL || !IS_OBJ_TYPE(obj, ITEM_SPELLBOOK))
	{
		pop_call();
		return FALSE;
	}

	if (!is_spell(sn))
	{
		pop_call();
		return FALSE;
	}

	if (obj->scribed[sn])
	{
		pop_call();
		return TRUE;
	}

	pop_call();
	return FALSE;
}


/*
 * Calculate the range of a spell based on range setting
 * and caster level of the spell - Kregor
 */
int get_spell_range( int level, int sn )
{
	int range;
	
	push_call("get_spell_range(%p,%p)",level,sn);
	
	if (IS_SET(skill_table[sn].flags, SF_CLOSE_RANGE))
	{
		range = level / 2 * 5 + 25;
	}
	else if (IS_SET(skill_table[sn].flags, SF_MEDIUM_RANGE))
	{
		range = level * 10 + 100;
	}
	else if (IS_SET(skill_table[sn].flags, SF_LONG_RANGE))
	{
		range = level * 40 + 400;
	}
	else
	{
		pop_call();
		return 0;
	}
	pop_call();
	return UMAX(1, range / 30); // range in room count = feet / 30 ft.
}


/*
 * Added value to Knowledge skills by granting
 * synergy bonus to the maximum hit dice of
 * assumed form based on the related knowledge skill - Kregor
 */
int polymorph_synergy( CHAR_DATA *ch, int race )
{
	int skill, ranks;
	
	push_call("polymorph_synergy()");
	
	switch (race_table[race].type)
	{
		case RTYPE_ABERRATION:
		case RTYPE_OOZE:
			skill = gsn_know_dungeoneering;
			break;
		case RTYPE_CONSTRUCT:
		case RTYPE_DRAGON:
		case RTYPE_MAGICAL:
			skill = gsn_know_arcana;
			break;
		case RTYPE_ANIMAL:
		case RTYPE_FEY:
		case RTYPE_MONSTROUS:
		case RTYPE_PLANT:
			skill = gsn_know_nature;
			break;
		case RTYPE_UNDEAD:
			skill = gsn_know_religion;
			break;
		case RTYPE_OUTSIDER:
			skill = gsn_know_planes;
			break;
	}
	
	if ((ranks = learned(ch, skill)) < 5)
	{
		pop_call();
		return 0;
	}
	pop_call();
	return (ranks / 5 + 1);
}


/*
 * Utter mystical words for an sn.
 * Revamped to include support for silent spell
 * and spells with no verbal component - Kregor
 */
void say_spell( CHAR_DATA *ch, int sn )
{
	char buf  [MAX_STRING_LENGTH];
	char buf2 [MAX_STRING_LENGTH];
	CHAR_DATA *rch;
	OBJ_DATA *scroll;
	char *pName;
	int iSyl, length, DC, level;
	bool spellcraft = FALSE;
	bool silent = FALSE;

	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"		},
		{ "hance",  "sch"			},
		{ "light",  "dies"		},
		{ "lo",     "hi"			},
		{ "mor",    "zak"			},
		{ "move",   "sido"		},
		{ "ob",     "kn"			},
		{ "ness",   "lacri"		},
		{ "ning",   "illa"		},
		{ "per",    "duda"		},
		{ "ra",     "gru"			},
		{ "re",     "candus"	},
		{ "son",    "sabru"		},
		{ "tect",   "infra"		},
		{ "tri",    "cula"		},
		{ "ven",    "nofo"		},
		{ "a", "a" }, { "b", "b" }, { "c", "q" }, { "d", "e" },
		{ "e", "z" }, { "f", "y" }, { "g", "o" }, { "h", "p" },
		{ "i", "u" }, { "j", "y" }, { "k", "t" }, { "l", "r" },
		{ "m", "w" }, { "n", "i" }, { "o", "a" }, { "p", "s" },
		{ "q", "d" }, { "r", "f" }, { "s", "g" }, { "t", "h" },
		{ "u", "j" }, { "v", "z" }, { "w", "x" }, { "x", "n" },
		{ "y", "l" }, { "z", "k" }, {  "", ""  }
	};

	push_call("say_spell(%p,%p)",ch,sn);

	if (sn == gsn_recite_scroll)
	{
		if ((scroll = ch->reciting) == NULL)
		{
			bug("say_spell: NULL scroll", 0);
			pop_call();
			return;
		}
		sn = scroll->value[1];
	}

	level = ch->cast_circle;

	buf[0]	= '\0';

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

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

	sprintf( buf2, "$n utters the words, %s'%s'.", get_color_string(ch, COLOR_SPEECH, VT102_DIM), buf );
	sprintf( buf,  "$n utters the words, %s'%s'.", get_color_string(ch, COLOR_SPEECH, VT102_DIM), skill_table[sn].name );
	
	/*
	   make wizcloaked gods even more invis,
	   their uttering cannot be heard...
	*/
	if (IS_PLR(ch, PLR_WIZCLOAK))
	{
		pop_call();
		return;
	}
	if (!IS_SET(skill_table[sn].flags, SF_VERBAL)
	|| IS_SET(ch->cast_feats, METAMAGIC_SILENT)
	|| IS_SET(ch->cast_feats, METAMAGIC_DISGUISE))
	{
		silent = TRUE;
	}

	if (!silent)
		act( "You utter the words, $t'$T'.", ch, get_color_string(ch, COLOR_SPEECH, VT102_DIM), skill_table[sn].name, TO_CHAR );

	DC = 15 + level;
	
	for (rch = ch->in_room->first_person ; rch ; rch = rch->next_in_room)
	{
		if (rch == ch)
			continue;
			
		if (!can_see_casting(rch, ch, sn))
			continue;

		if (IS_NPC(rch) || IS_PLR(rch, PLR_HOLYLIGHT) || spellcraft_check(rch, ch, sn, spellcraft_roll(rch), DC))
			spellcraft = TRUE;
			
		if (spellcraft)
			act( "You guess that $n is casting $t.", ch, skill_table[sn].name, rch, TO_VICT);

		if (silent)
			continue;
			
		if (spellcraft)
		{
			act( buf,  ch, NULL, rch, TO_VICT);
		}
		else
		{
			act( buf2, ch, NULL, rch, TO_VICT);
		}
	}
	pop_call();
	return;
}

/*
 * Look for components either in inventory, or in spellpouch,
 * fCast FALSE checks for presence at the beginning of cast,
 * TRUE decrements the component uses at release_cast - Kregor 3/2/10
 */
bool check_components( CHAR_DATA *ch, int sn, bool fCast )
{
	OBJ_DATA *component, *cont;
	int cnt, comp;
	bool found = TRUE;
	
	push_call("check_components(%p,%p,%p)",ch,sn,fCast);

	if (sn <= 0)
	{
		pop_call();
		return TRUE;
	}
	
	// imms and npcs don't need components
	if (IS_NPC(ch) || IS_PLR(ch, PLR_HOLYLIGHT))
	{
		pop_call();
		return TRUE;
	}
	
	for (cnt = 0 ; cnt < 5 ; cnt++)
	{
		component = NULL;
		
		if ((comp = skill_table[sn].components[cnt]) <= 0) // if component is null, go no further
			continue;
			
		//skip component w/ eschew materials, if 1 gp or less
		if (learned(ch, gsn_eschew_materials) && component_table[comp].cost <= 100)
			continue;
			
		if (IS_AFFECTED(ch, AFF_GASEOUS))
		{
			act("You cannot grab your components in your physical state.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}

		//keep from crashing if calls to NULL component table entry
		if (!is_string(component_table[comp].name))
		{
			bug("check_components: %s bad component type %d", skill_table[sn].name, comp);
			pop_call();
			return FALSE;
		}

		for (component = ch->first_carrying ; component ; component = component->next_content)
		{
			if ((component->item_type == ITEM_COMPONENT && comp == component->value[0])
			||(component->item_type == ITEM_TREASURE && comp == component->value[5]))
			{
				break;
			}
			if (component->item_type == ITEM_SPELLPOUCH && !IS_SET(component->value[1], CONT_CLOSED))
			{
				cont = component;

				for (component = component->first_content ; component ; component = component->next_content)
				{
					if ((component->item_type == ITEM_COMPONENT && comp == component->value[0])
					||(component->item_type == ITEM_TREASURE && comp == component->value[5]))
					{
						break;
					}
				}
				if (component)
				{
					break;
				}
				else
				{
					component = cont;
				}
			}
		} 
		if (component == NULL)
		{
			ch_printf_color(ch, "You are missing your %s.\n\r", component_table[comp].name);
			found = FALSE;
			continue;
		}
		if (fCast && --component->value[1] <= 0)
		{
			act("$p {108}is consumed in a whisp of smoke.", ch, NULL, NULL, TO_CHAR);
			act("$p {108}is comsumed in a whisp of smoke.", ch, NULL, NULL, TO_ROOM);
			junk_obj(component);
		}
	}
	pop_call();
	return found;
}


/* 
 * LEARN command for learning spells for any class
 * that has to learn some or all of their spells.
 * Also learning bard songs for bards.
 * Wizards will have the requirement of a spellbook,
 * Sorcerers and Bards have their number
 * limited to their constant table - Kregor 12/3/07
 */
void do_learn( CHAR_DATA *ch, char *argument )
{
	CHAR_DATA *mob = NULL;
	OBJ_DATA *book, *scroll, *spellbook;
	char arg[MAX_INPUT_LENGTH];
	char classname[MAX_INPUT_LENGTH];
	char spellname[MAX_INPUT_LENGTH];
	char adj[MAX_INPUT_LENGTH];
	int sn, class, slot, stat, cost, diceroll;
	
	push_call("do_learn(%p)",ch,argument);
	
	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}
	
	if (!is_caster(ch))
	{
		send_to_char("You don't have any spells to learn.\n\r", ch);
		pop_call();
		return;
	}
	
	if (argument[0] == '\0')
	{
		send_to_char("Syntax: learn <'spell name'> [class]\n\r", ch);
		send_to_char("Syntax: learn <'song name'>\n\r", ch);
		pop_call();
		return;
	}
	
	argument = one_argument(argument, arg);
	
	if ((sn = skill_lookup(arg)) == -1)
	{
		send_to_char("That is not a spell.\n\r", ch);
		pop_call();
		return;
	}

	if (skill_table[sn].skilltype == FSKILL_BARDSONG && !learned(ch, gsn_bardic_song))
	{
		send_to_char("You cannot learn any bardic songs.\n\r", ch);
		pop_call();
		return;
	}

	if (!is_spell(sn) && skill_table[sn].skilltype != FSKILL_BARDSONG)
	{
		send_to_char("That is not a spell or song.\n\r", ch);
		pop_call();
		return;
	}

	sprintf(spellname, "%s", skill_table[sn].name);

	if (skill_table[sn].skilltype != FSKILL_BARDSONG)
	{
		if ((scroll = get_obj_char_type(ch, ITEM_SCROLL)) == NULL 
		|| scroll->value[1] <= 0 || skill_table[scroll->value[1]].skilltype != FSKILL_BARDSONG)
		{
			for (mob = ch->in_room->first_person ; mob ; mob = mob->next_in_room)
			{
				if (IS_NPC(mob) && IS_SET(mob->act, ACT_TRAIN))
				{
					break;
				}
			}
			if (mob == NULL)
			{
				ch_printf(ch, "You need sheet music, or a teacher to learn %s from.\n\r", spellname);
				pop_call();
				return;
			}
			if (!learned(mob, sn))
			{
				act("$N does not know $t to teach it to you.", ch, spellname, mob, TO_CHAR);
				pop_call();
				return;
			}
		}
		// adds a cost for learning from mobile
		if (mob)
		{
			if ((cost = skill_table[sn].native_level * 10000) > ch->gold)
			{
				act("You need $t to learn that song from $N.", ch, format_coins(cost, TRUE), mob, TO_CHAR);
				pop_call();
				return;
			}
		}
		if (learned(ch, sn))
		{
			ch_printf(ch, "You already know %s.\n\r", spellname);
			pop_call();
			return;
		}
		// must have enough perform ranks to learn it
		if (skill_table[sn].native_level > learned(ch, gsn_perform))
		{
			ch_printf(ch, "%s is too powerful for you to perform.\n\r", spellname);
			pop_call();
			return;
		}
		ch->learned[sn] = 1;
		act( "You learn to perform $t.", ch, spellname, NULL, TO_CHAR);
		ch->pcdata->skill_level[ch->level][sn] = 1;
		
		if (scroll)
		{
			act("{138}The staves on $p glow brightly, then vanish!", ch, scroll, NULL, TO_CHAR);
			junk_obj(scroll);
		}
		pop_call();
		return;
	}

	if (argument[0] == '\0')
	{
		if ((class = multi(ch, sn)) == -1)
		{
			ch_printf_color(ch, "You cannot learn %s in any of your classes.\n\r", spellname);
			pop_call();
			return;
		}
	}
	else
	{
		if ((class = lookup_class(argument)) == -1)
		{
			send_to_char("That is not a class.\n\r", ch);
			pop_call();
			return;
		}
	}
	
	if (learned_in_class(ch, sn, class))
	{
		ch_printf_color(ch, "You already know %s in that class.\n\r", spellname);
		pop_call();
		return;
	}
	
	sprintf(classname, "%s", class_table[class].who_name_long);

	if ((slot = circle_by_class(ch, sn, class)) == -1
	|| class_table[class].mana_table == MANA_NONE
	|| !class_level(ch, class))
	{
		ch_printf_color(ch, "You cannot learn %s as a %s.\n\r", spellname, classname);
		pop_call();
		return;
	}
	
	switch (class_table[class].attr_prime)
	{
		case APPLY_WIS:
			send_to_char("You are given spells by your diety. You don't need to learn.\n\r", ch);
			pop_call();
			return;
		case APPLY_INT:
			stat = get_curr_int(ch);
			sprintf(adj, "intelligent");
			break;
		case APPLY_CHA:
			stat = get_curr_cha(ch);
			sprintf(adj, "charismatic");
			break;
	}
	if (slot > stat - 10)
	{
		ch_printf_color(ch, "You are not %s enough to learn %s.\n\r", adj, spellname);
		pop_call();
		return;
	}
	
	if (max_spell_circle(ch, class) < slot)
	{
		ch_printf_color(ch, "Your %s level is not high enough to learn %s.\n\r", classname, spellname);
		pop_call();
		return;
	}
	
	if (class_table[class].attr_prime == APPLY_CHA)
	{
		if (slots_learned(ch, class, slot) >= can_learn_spells(ch, class, slot))
		{
			ch_printf_color(ch, "You cannot learn any more %d circle %s spells.\n\r", slot, classname);
			pop_call();
			return;
		}
	}
	if (class == CLASS_WIZARD)
  {
  	book = NULL;
  	scroll = NULL;
  	
		if ((spellbook = get_obj_wear_type(ch, ITEM_SPELLBOOK)) == NULL)
		{
			send_to_char("You are not holding your spellbook to scribe into.\r\n", ch);
			pop_call();
			return;
		}
		if (!IS_OWNER(spellbook, ch))
		{
			send_to_char("That is not your spellbook to scribe into.\r\n", ch);
			pop_call();
			return;
		}

		if (opposing_school(ch, sn))
		{
			send_to_char("You cannot scribe a spell of a prohibited school.\r\n", ch);
			pop_call();
			return;
		}
		
		cost = slot * 10000;
		
		if (cost > ch->gold)
		{
			act("You need $t on hand to scribe $T into your spellbook.", ch, format_coins(cost, TRUE), spellname, TO_CHAR);
			pop_call();
			return;
		}

		if (!ch->pcdata->bonus_spells)
		{
			if ((scroll = get_obj_carry_type(ch, ITEM_SCROLL)) == NULL)
			{
				if ((book = get_obj_carry_type(ch, ITEM_SPELLBOOK)) == NULL || IS_WORN(book))
				{
					send_to_char("You are not carrying scroll or spellbook to learn from.\n\r", ch);
					pop_call();
					return;
				}
			}
			
			if (!is_affected(ch, gsn_read_magic))
			{
				send_to_char("You must be affected by Read magic to transcribe a spell.\r\n", ch);
				pop_call();
				return;
			}
			
			if (scroll)
			{
				if (!scroll->identified)
				{
					send_to_char("You've not identified the writing on this scroll.\r\n", ch);
					pop_call();
					return;
				}
				if (scroll->value[1] != sn)
				{
					send_to_char("That spell is not contained on this scroll.\r\n", ch);
					pop_call();
					return;
				}
			}
			else if (book)
			{
				if (!spell_in_book(scroll, sn))
				{
					send_to_char("That spell is not in this spellbook!\r\n", ch);
					pop_call();
					return;
				}
			}
			
			diceroll = spellcraft_roll(ch);
	
			if (spellcraft_check(ch, NULL, sn, diceroll, slot))
			{
				act( "You fail to sufficiently comprehend the writing on $p.", ch, scroll, NULL, TO_CHAR);
				act( "$n tries to scribe $p on $P, but fails.", ch, scroll, spellbook, TO_ROOM);
				ch->gold -= cost / 2;
				act( "You waste $t in the failed scribing attempt.", ch, format_coins(cost/2, TRUE), NULL, TO_CHAR);
				pop_call();
				return;
			}
		}
		else
		{
			if (get_school(ch) != SCHOOL_NONE && skill_table[sn].spell_school != get_school(ch))
			{
				act("Your bonus spells must come from your specialty school.", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return;
			}
		}
				
		act( "You scribe $T into $p.", ch, spellbook, spellname, TO_CHAR);
		act( "$n scribes $T into $p.", ch, spellbook, spellname, TO_ROOM);
		ch->gold -= cost;
		act( "You spend $t in materials to scribe in your spellbook.", ch, format_coins(cost, TRUE), NULL, TO_CHAR);
		
		if (scroll)
		{
			act( "The writing on $p glows brightly before fading away.", ch, scroll, NULL, TO_CHAR);
			act( "The writing on $p glows brightly before fading away.", ch, scroll, NULL, TO_ROOM);
			junk_obj(scroll);
		}
		else if (book)
		{
			act( "The writing on $p glows brightly before fading away.", ch, book, NULL, TO_CHAR);
			act( "The writing on $p glows brightly before fading away.", ch, book, NULL, TO_ROOM);
			book->scribed[sn] = 0;
		}
		
		spellbook->scribed[sn] = 1;
		if (!spellbook->owned_by)
			spellbook->owned_by = ch->pcdata->pvnum;
		if (ch->pcdata->bonus_spells)
			ch->pcdata->bonus_spells--;
	}

// 	ch->learned[sn] += 1 << class;
	SET_BIT(ch->learned[sn], 1 << class);
	act( "You learn $t as a $T spell.", ch, spellname, classname, TO_CHAR);
	SET_BIT(ch->pcdata->skill_level[ch->level][sn], 1 << class);
	
	pop_call();
	return;
}


/*
 * Allows non-spontaneous casters to prepare
 * their spells into slots - Kregor
 */
void do_prepare( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	char classname[MAX_INPUT_LENGTH];
	char adj[MAX_INPUT_LENGTH];
	int sn, class, slot, school, stat, cls;
	
	push_call("do_prepare(%p)",ch,argument);
	
	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}
	
	if (!is_caster(ch))
	{
		send_to_char("You don't have any spells to prepare.\n\r", ch);
		pop_call();
		return;
	}
	
	if (ch->position > POS_RESTING)
	{
		send_to_char("You must be resting to prepare spells.\n\r", ch);
		pop_call();
		return;
	}
		
	/*
	 * simple enough solution for regulating spell preparation,
	 * can only be done at max mana, thus, after a full 8-hour rest.
	 */
	for (cls = 0 ; cls < MAX_CLASS ; cls++)
	{
		if (!class_level(ch, cls))
			continue;
			
		if (ch->mana[cls] < get_max_mana(ch, cls))
		{
			send_to_char("You can only prepare spells when fully rested.\n\r", ch);
			pop_call();
			return;
		}
	}

	if (argument[0] == '\0')
	{
		send_to_char("Syntax: prepare <'spell name'> [class]\n\r", ch);
		pop_call();
		return;
	}
	
	argument = one_argument(argument, arg);
	
	if ((sn = skill_lookup(arg)) == -1 || !is_spell(sn))
	{
		send_to_char("That is not a spell.\n\r", ch);
		pop_call();
		return;
	}

	if (prepared(ch, sn) != -1)
	{
		ch_printf_color(ch, "That spell is already prepared as a %s spell.\n\r", class_table[prepared(ch, sn)].who_name_long);
		send_to_char("You have to FORGET it first if you want to re-prepare it.\n\r", ch);
		pop_call();
		return;
	}

	if (argument[0] == '\0')
	{
		if ((class = multi(ch, sn)) == -1)
		{
			send_to_char("You cannot prepare that spell in any of your classes.\n\r", ch);
			pop_call();
			return;
		}
	}
	else
	{
		if ((class = lookup_class(argument)) == -1)
		{
			send_to_char("That is not a class.\n\r", ch);
			pop_call();
			return;
		}
	}

	if (!learned_in_class(ch, sn, class))
	{
		send_to_char("You don't know that spell in that class.\n\r", ch);
		pop_call();
		return;
	}
	
	if ((slot = circle_by_class(ch, sn, class)) == -1)
	{
		if (spontaneous_cast(ch, sn) != -1)
		{
			send_to_char("You cast that spell spontaneously. You don't need to prepare it.\n\r", ch);
			pop_call();
			return;
		}
		else
		{
			send_to_char("You cannot prepare that spell in that class.\n\r", ch);
			pop_call();
			return;
		}
	}
	
	switch (class_table[class].attr_prime)
	{
		case APPLY_WIS:
			stat = get_curr_wis(ch);
			sprintf(adj, "wise");
			break;
		case APPLY_INT:
			stat = get_curr_int(ch);
			sprintf(adj, "intelligent");
			break;
		case APPLY_CHA:
			stat = get_curr_cha(ch);
			sprintf(adj, "charismatic");
			break;
	}
	if (slot > stat - 10)
	{
		ch_printf_color(ch, "You are not %s enough to prepare that spell.\n\r", adj);
		pop_call();
		return;
	}
	
	if (max_spell_circle(ch, class) < slot)
	{
		send_to_char("Your class level is not high enough.\n\r", ch);
		pop_call();
		return;
	}
	
	if (slots_full(ch, class, slot) >= get_slot_count(ch, class, slot))
	{
		send_to_char("You cannot prepare any more spells of that level in that class.\n\r", ch);
		pop_call();
		return;
	}
	
	if (class == CLASS_WIZARD)
	{
		OBJ_DATA *spellbook;
		
		if ((spellbook = get_obj_wear_type(ch, ITEM_SPELLBOOK)) == NULL || !IS_OWNER(spellbook, ch))
		{
			send_to_char("You are not holding your spellbook to prepare from.\r\n", ch);
			pop_call();
			return;
		}
		if (!spell_in_book(spellbook, sn))
		{
			send_to_char("That spell is not in your spellbook!\r\n", ch);
			pop_call();
			return;
		}
		if (opposing_school(ch, sn))
		{
			send_to_char("You cannot prepare a spell of your opposing school of magic.\r\n", ch);
			pop_call();
			return;
		}
		if ((school = get_school(ch)) > 0 && slot != 0 && skill_table[sn].spell_school != school)
		{
			if (slots_full(ch, class, slot) + 1 >= get_slot_count(ch, class, slot) && !prepared_school(ch, slot))
			{
				send_to_char("You must prepare at least one spell in your school for each spell level.\n\r", ch);
				pop_call();
				return;
			}
		}
	}

	ch->pcdata->prepared[sn] = class;
	sprintf(classname, "%s", class_table[class].who_name_long);
	
	ch_printf_color(ch, "You prepare %s as %s %s spell.\n\r",
		skill_table[sn].name, a_an(classname), classname);
	act( "$n meditates and prepares a spell.", ch, NULL, NULL, TO_ROOM);
	
	pop_call();
	return;
}


/*
 * And... forgetting the spell from a prepared slot - Kregor
 */
void do_forget( CHAR_DATA *ch, char *argument )
{
	int sn, class, level;
	char arg[MAX_INPUT_LENGTH];
	
	push_call("do_forget(%p,%p)",ch,argument);
	
	sn = class = level = -1;
	
	if (IS_NPC(ch))
	{
		pop_call();
		return;
	}

	if (ch->position != POS_RESTING)
	{
		send_to_char("You must be resting to prepare spells.\n\r", ch);
		pop_call();
		return;
	}
	
	if (argument[0] == '\0')
	{
		send_to_char("Syntax forget <all|class|'spell name'> [circle]\n\r", ch);
		pop_call();
		return;
	}
	
	argument = one_argument(argument, arg);
	
	if (!strcasecmp(arg, "all"))
	{
		send_to_char("You purge all your spells from memory.\n\r", ch);
		
		for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
		{
			if (!is_spell(sn))
				continue;

			ch->pcdata->prepared[sn] = -1;
		}
		pop_call();
		return;
	}			
	else if ((class = lookup_class(arg)) != -1)
	{
		if (class_level(ch, class) <= 0)
		{
			ch_printf_color(ch, "You aren't a member of the %s class.\n\r", class_table[class].who_name_long);
			pop_call();
			return;
		}
		
		if (argument[0] != '\0')
		{
			if (!is_number(argument))
			{
				send_to_char("The second argument must be a number.\n\r", ch);
				send_to_char("Syntax forget class [circle]\n\r", ch);
				pop_call();
				return;
			}
			if ((level = atol(argument)) < 0 || level > max_spell_circle(ch, class))
			{
				ch_printf_color(ch, "Value must be between 0 and %d.\n\r", max_spell_circle(ch, class));
				level = -1;
				pop_call();
				return;
			}
		}

		if (level == -1)
			ch_printf_color(ch, "You purge all your %s spells from memory.\n\r", class_table[class].who_name_long);
		else
			ch_printf_color(ch, "You purge all your %s circle %s spells from memory.\n\r", numbersuf(level), class_table[class].who_name_long);

		for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
		{
			if (!is_spell(sn))
				continue;
				
			if (prepared(ch, sn) != class)
				continue;
				
			if (level != -1 && circle_by_class(ch, sn, class) != level)
				continue;

			ch->pcdata->prepared[sn] = -1;
		}
		pop_call();
		return;
	}	
	else if ((sn = skill_lookup(arg)) == -1 || !is_spell(sn))
	{
		ch_printf_color(ch, "%s is not a spell.\n\r", arg);
		pop_call();
		return;
	}
	
	if (ch->pcdata->prepared[sn] <= 0)
	{
		ch_printf_color(ch, "You do not have %s prepared.\n\r", skill_table[sn].name);
		pop_call();
		return;
	}
	
	if ((class = prepared(ch, sn)) && !class_level(ch, class))
	{
		send_to_char("Something has gone wrong...\n\r", ch);
		bug("do_forget: %s - forgetting spell %s in a class it doesn't have.\n\r", ch->name, skill_table[sn].name);
		pop_call();
		return;
	}
	
	ch_printf_color(ch, "You purge %s from your prepared %s spells.\n\r", skill_table[sn].name, class_table[class].who_name_long);
	ch->pcdata->prepared[sn] = 0;
	
	pop_call();
	return;
}


/*
 * Determines any metamagic which could not be applied to spell.
 * Will alter current casting metamagic feats. fCasting FALSE
 * will return the effective spell circle instead, used for get_mana
 * calculations.
 */
int get_metamagic( CHAR_DATA *ch, int sn, int feats, int circle, int class, bool fCasting )
{
	int eff_circle, flags, max_circle;
	char buf[MAX_INPUT_LENGTH];

	push_call("get_metamagic_level(%p,%p,%p,%p,%p)",ch,sn,feats,class,fCasting);

	flags = skill_table[sn].flags;
	sprintf(buf, "%s", skill_table[sn].name);

	max_circle = max_spell_circle(ch, class);
			
	if (IS_SET(feats, METAMAGIC_DISGUISE))
	{
		if ((eff_circle = circle + 1) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot disguise '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_DISGUISE);
		}
		else
		{
			circle += 1;
		}
	}
	if (IS_SET(feats, METAMAGIC_EMPOWER))
	{
		if (IS_SET(flags, SF_NOVARIABLE))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot empower '%s'; spell has no variable affect.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_EMPOWER);
		}
		else if (strstr(skill_table[sn].name, "cure") && learned(ch, gsn_empower_curing))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot empower '%s'; does not stack with Empower Curing.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_EMPOWER);
		}
		else if (strstr(skill_table[sn].name, "inflict") && learned(ch, gsn_empower_infliction))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot empower '%s'; does not stack with Empower Infliction.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_EMPOWER);
		}
		else if ((eff_circle = circle + 2) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot empower '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_EMPOWER);
		}
		else
		{
			circle += 2;
		}
	}
	if (IS_SET(feats, METAMAGIC_ENLARGE))
	{
		if (!IS_SET(flags, SF_CLOSE_RANGE)
		&& !IS_SET(flags, SF_MEDIUM_RANGE)
		&& !IS_SET(flags, SF_LONG_RANGE))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot enlarge '%s'; spell has no range.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_ENLARGE);
		}
		else if ((eff_circle = circle + 1) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot enlarge '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_ENLARGE);
		}
		else
		{
			circle += 1;
		}
	}
	if (IS_SET(feats, METAMAGIC_PERSIST))
	{
		if (IS_SET(flags, SF_INSTANT))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot persist '%s'; spell has no duration.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_PERSIST);
		}
		else if (!IS_SET(flags, SF_PERSONAL))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot persist '%s'; it is not a personal spell.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_PERSIST);
		}
		else if ((eff_circle = circle + 6) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot persist '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_PERSIST);
		}
		else
		{
			circle += 6;
		}
	}
	if (IS_SET(feats, METAMAGIC_EXTEND))
	{
		if (IS_SET(feats, METAMAGIC_PERSIST))
		{
			if (fCasting)
				ch_printf_color(ch, "Persistent Spell overrides your Extension of '%s'.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_EXTEND);
		}
		else if (IS_SET(flags, SF_INSTANT))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot extend '%s'; spell has no duration.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_EXTEND);
		}
		else if (IS_SET(skill_table[sn].spell_desc, SDESC_POLYMORPH) && get_bloodline(ch) == BLOODLINE_ABERRANT)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot extend '%s'; does not stack with your bloodline power.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_EXTEND);
		}
		else if ((eff_circle = circle + 1) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot extend '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_EXTEND);
		}
		else
		{
			circle += 1;
		}
	}
	if (IS_SET(feats, METAMAGIC_MAXIMIZE))
	{
		if (IS_SET(flags, SF_NOVARIABLE))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot maximize '%s'; spell has no variable affect.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_MAXIMIZE);
		}
		else if (strstr(skill_table[sn].name, "cure") && domain_apotheosis(ch, DOMAIN_HEALING))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot maximize '%s'; your domain already maximizes it.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_EMPOWER);
		}
		else if ((eff_circle = circle + 3) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot maximize '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_MAXIMIZE);
		}
		else
		{
			circle += 3;
		}
	}
	if (IS_SET(feats, METAMAGIC_MERCIFUL))
	{
		if (skill_table[sn].dam_type == DAM_NONE)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot make '%s' merciful; not a damaging spell.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_MERCIFUL);
		}
		else if (IS_SET(skill_table[sn].spell_desc, SDESC_DEATH))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot make '%s' merciful; it's an instant death affect.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_MERCIFUL);
		}
	}
	if (IS_SET(feats, METAMAGIC_QUICKEN))
	{
		if (skill_table[sn].beats <= CASTING_SWIFT)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot quicken '%s'; it is already a swift action.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_QUICKEN);
		}
		else if (IS_SET(ch->action, ACTION_SWIFT))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot quicken '%s'; you have already taken a swift action.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_QUICKEN);
		}
		else if ((eff_circle = circle + 4) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot quicken '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_QUICKEN);
		}
		else
		{
			circle += 4;
		}
	}
	if (IS_SET(feats, METAMAGIC_REACH))
	{
		if (!IS_SET(flags, SF_TOUCH))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot reach cast '%s'; it is not a touch spell.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_REACH);
		}
		else if ((eff_circle = circle + 2) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot reach cast '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_REACH);
		}
		else
		{
			circle += 2;
		}
	}
	if (IS_SET(feats, METAMAGIC_REPEAT))
	{
		if (!IS_SET(flags, SF_CLOSE_RANGE)
		&& !IS_SET(flags, SF_MEDIUM_RANGE)
		&& !IS_SET(flags, SF_CLOSE_RANGE))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot repeat '%s'; spell has no range.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_REPEAT);
		}
		else if ((eff_circle = circle + 3) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot repeat '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_REPEAT);
		}
		else
		{
			circle += 1;
		}
	}
	if (IS_SET(feats, METAMAGIC_SACRED))
	{
		if (!IS_SET(skill_table[sn].dam_type, DAM_COLD)
		&& !IS_SET(skill_table[sn].dam_type, DAM_ELECTRIC)
		&& !IS_SET(skill_table[sn].dam_type, DAM_FIRE)
		&& !IS_SET(skill_table[sn].dam_type, DAM_ACID)
		&& !IS_SET(skill_table[sn].dam_type, DAM_SONIC))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot make '%s' sacred; not a damaging energy spell.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_SACRED);
		}
		else if ((eff_circle = circle + 2) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot make '%s' sacred, as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_SACRED);
		}
		else
		{
			circle += 2;
		}
	}
	if (IS_SET(feats, METAMAGIC_SILENT))
	{
		if (!IS_SET(flags, SF_VERBAL))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot silence '%s'; it has no verbal component.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_SILENT);
		}
		else if (multi(ch, sn) == CLASS_BARD)
		{
			if (fCasting)
				ch_printf_color(ch, "You cannot cast bardic magic silently.\n\r");
			REMOVE_BIT(feats, METAMAGIC_SILENT);
		}
		else if ((eff_circle = circle + 1) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot silence '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_SILENT);
		}
		else
		{
			circle += 1;
		}
	}
	if (IS_SET(feats, METAMAGIC_STILL))
	{
		if (!IS_SET(flags, SF_SOMATIC))
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot still '%s'; it has no somatic component.\n\r", buf);
			REMOVE_BIT(feats, METAMAGIC_STILL);
		}
		else if ((eff_circle = circle + 1) > max_circle)
		{
			if (fCasting)
				ch_printf_color(ch, "Cannot still '%s', as it would need a %s circle slot.\n\r", buf, numbersuf(eff_circle));
			REMOVE_BIT(feats, METAMAGIC_STILL);
		}
		else
		{
			circle += 1;
		}
	}
	if (fCasting)
	{
		pop_call();
		return feats;
	}
	pop_call();
	return circle;
}

/* 
	Bardic song ability by Kregor
	Song defaults to morale song, with argument will call 
	bardic songs learned similar to spell funs.
*/
void do_sing (CHAR_DATA * ch, char *argument)
{
	CHAR_DATA *victim;
	void *vo = NULL;
	char arg[MAX_INPUT_LENGTH];
	int sn, level, diceroll, target;

	push_call("do_sing(%p,%p)",ch,argument);

	if (ch == NULL)
	{
		pop_call();
		return;
	}

	if (!learned(ch, gsn_bardic_song))
	{
		send_to_char ("You do not possess bardic song ability.\n\r", ch);
		pop_call();
		return;
	}
	
	if (!CHECK_USES(ch, gsn_bardic_song))
	{
		pop_call();
		return;
	}
	
	if (learned(ch, gsn_perform) < 3)
	{
		send_to_char ("You have to have at least 3 ranks in perform to sing a bard song.\n\r", ch);
		pop_call();
		return;
	}
	
	if (!IS_NPC(ch) && IS_SET(pvnum_index[ch->pcdata->pvnum]->flags, PVNUM_SILENCED))
	{
		send_to_char ("You cannot sing.\n\r", ch);
		pop_call();
		return;
	}

	if (!CAN_TALK(ch))
	{
		send_to_char ("You are unable to sing.\n\r", ch);
		pop_call();
		return;
	}
	
	one_argument(argument, arg);

	if (arg[0] != '\0')
	{
		if ((sn = skill_lookup(arg)) < 0 || skill_table[sn].skilltype != FSKILL_BARDSONG)
		{
			send_to_char ("That is not a bard song.\n\r", ch);
			pop_call();
			return;
		}
	}
	else
	{
		sn = gsn_song_of_inspiration;
	}

	if (!learned(ch, sn))
	{
		send_to_char ("You don't know that song.\n\r", ch);
		pop_call();
		return;
	}

	if (is_safe_magic(ch, NULL, sn))
	{
		pop_call();
		return;
	}

	level = multi_skill_level(ch, gsn_bardic_song);
	diceroll = perform_roll(ch);
	if (learned(ch, gsn_empower_song))
		diceroll += 4;

	act( "{138}$n starts to sing...", ch, NULL, NULL, TO_ROOM );
	act( "{138}You start to sing...", ch, NULL, NULL, TO_CHAR );

	target = skill_table[sn].target;

	switch (target)
	{
		default:
			bug( "do_sing: bad target for sn %d.", sn );
			pop_call();
			return;

		case TAR_IGNORE:
			break;

		case TAR_CHAR_OFFENSIVE:
			if ((victim = who_fighting(ch)) == NULL)
			{
				send_to_char( "You're not fighting anyone.\n\r", ch );
				pop_call();
				return;
			}
			if (is_safe_magic(ch, victim, sn))
			{
				pop_call();
				return;
			}
			vo = (void *) victim;
			break;

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

	(*skill_table[sn].spell_fun) (sn, level, ch, vo, target);

	ch->uses[gsn_bardic_song]++;
	if (learned(ch, gsn_magna_alumnus))
		TAKE_ACTION(ch, ACTION_SWIFT);
	else if (learned(ch, gsn_quicken_song))
		TAKE_ACTION(ch, ACTION_MOVE);
	else
		TAKE_ACTION(ch, ACTION_STANDARD);
	
	pop_call();
	return;
}


/*
 * check to see if a ranged spell can and
 * does affect a door in given direction - Kregor
 */
bool check_cast_door( CHAR_DATA *ch, int dir, int sn, int level, bool ForReal )
{
	EXIT_DATA *pExit, *pExit_rev;
	ROOM_TIMER_DATA *rtd;
	bool IsDoor = FALSE;
	int hp, hardness, dam, maxdice, nodice, dam_type;

	push_call("check_cast_door(%p,%p,%p,%p)",ch,dir,sn,ForReal);
	
	if ((pExit = get_exit(ch->in_room->vnum, dir)) == NULL)
	{
		if (!ForReal)
			act("There's no exit in that direction!", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if ((rtd = get_room_affect(ch->in_room, ROOM_BLOCK)) == NULL || (rtd->modifier != -1 && rtd->modifier != dir))
	{
		if ((rtd = get_room_affect(room_index[pExit->to_room], ROOM_BLOCK)) == NULL || (rtd->modifier != -1 && rtd->modifier != rev_dir[dir]))
		{
			if (IS_SET(pExit->exit_info, EX_BASHED) || !IS_SET(pExit->exit_info, EX_CLOSED))
			{
				if (!ForReal)
					act("There's nothing blocking that direction!", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return FALSE;
			}
			else
			{
				IsDoor = TRUE;
			}
		}
	}
	
	if (skill_table[sn].target != TAR_CHAR_OFFENSIVE && sn != gsn_dispel_magic && sn != gsn_greater_dispel && sn != gsn_disjunction)
	{
		act("Your $t will have no effect on that barrier!", ch, skill_table[sn].noun_damage, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	dam_type = skill_table[sn].dam_type;

	if ((!IS_SET(dam_type, DAM_PIERCE|DAM_SLASH|DAM_BASH|DAM_ACID|DAM_FIRE|DAM_FORCE|DAM_SONIC)
	&& sn != gsn_disintegrate) || IS_SET(dam_type, DAM_NONLETHAL))
	{
		if ((!rtd && !IS_SET(pExit->exit_info, EX_MAGICAL_LOCK)) || (sn != gsn_dispel_magic && sn != gsn_greater_dispel && sn != gsn_disjunction))
		{
			if (!ForReal)
				act("Your $t will have no effect on that barrier!", ch, skill_table[sn].noun_damage, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act("You really cast in that direction!", ch, NULL, NULL, TO_CHAR);
	
	maxdice = UMAX(skill_table[sn].native_level, skill_table[sn].mana_move) * 2;
	nodice = UMIN(level, maxdice);
	if (sn == gsn_disintegrate)
		dam = spell_dice(ch, sn, level * 2, 6);
	else
	{
		dam = spell_dice(ch, sn, nodice, 6);
		/*
		 * barrier is always an object, so here we calc different damage types rules vs. objects
		 */
		if (!IS_SET(dam_type, DAM_PHYSICAL|DAM_ACID|DAM_SONIC|DAM_FORCE)) // These do full damage to objects
		{
			if (IS_SET(dam_type, DAM_ELECTRIC|DAM_FIRE))
				dam /= 2;
			else
				dam /= 4;
		}
	}
	
	if (rtd)
	{
		bool Broken = FALSE;

		if (sn != gsn_dispel_magic && sn != gsn_greater_dispel && sn != gsn_disjunction)
		{
			if (rtd->type == gsn_wall_of_ice)
			{
				hp = rtd->level * 3;
				hardness = 0;
				if (IS_SET(dam_type, DAM_FIRE))
					dam *= 2;
			}
			else if (rtd->type == gsn_wall_of_stone)
			{
				hp = rtd->level * 15 / 4;
				hardness = 8;
			}
			else if (rtd->type == gsn_wall_of_iron)
			{
				hp = rtd->level * 30 / 4;
				hardness = 10;
			}
			else
			{
				send_to_char( "The barrier cannot be forced open.\n\r", ch );
				pop_call();
				return TRUE;
			}
			if (!IS_SET(dam_type, DAM_ACID|DAM_SONIC))
			{
				dam = UMAX(0, dam - hardness);
			}
			
			wiz_printf_room(ch, "Damage: %d vs. Hitpoints: %d\n\r", dam, hp);
			
			if (dam >= hp)
			{
				act("{118}The $t is sundered by $n's $T!", ch, skill_table[rtd->type].noun_damage, skill_table[sn].noun_damage, TO_ROOM);
				act("{118}The $t is sundered by your $T!", ch, skill_table[rtd->type].noun_damage, skill_table[sn].noun_damage, TO_CHAR);
				Broken = TRUE;
			}
			else
			{
				act("{118}The $t stands firm against $n's $T!", ch, skill_table[rtd->type].noun_damage, skill_table[sn].noun_damage, TO_ROOM);
				act("{118}The $t stands firm against your $T!", ch, skill_table[rtd->type].noun_damage, skill_table[sn].noun_damage, TO_CHAR);
				pop_call();
				return TRUE;
			}
		}
		else
		{
			if (sn == gsn_disjunction || dice(1,20) + level >= 11 + rtd->level)
			{
				act("{178}The $t is dispelled from existence!", ch, skill_table[rtd->type].noun_damage, NULL, TO_ALL);
				Broken = TRUE;
			}
		}
		if (Broken)
		{
			del_room_timer(rtd->vnum, rtd->type);
		}
	}
				
	if (IsDoor)
	{
		if (sn == gsn_dispel_magic && sn == gsn_greater_dispel && sn == gsn_disjunction)
		{
			if (!IS_SET(pExit->exit_info, EX_MAGICAL_LOCK))
			{
				act("Your $T has no effect on the $d", ch, pExit->keyword, skill_table[sn].noun_damage, TO_CHAR);
				pop_call();
				return TRUE;
			}
			else
			{
				REMOVE_BIT(pExit->exit_info, EX_MAGICAL_LOCK);
				act("You dispel the arcane lock upon the $d.", ch, pExit->keyword, NULL, TO_CHAR);
				pop_call();
				return TRUE;
			}
		}
		else
		{
			act("{118}BLAAAAM!!!{300}", ch, NULL, NULL, TO_CHAR);
			act("{118}BLAAAAM!!!{300}", ch, NULL, NULL, TO_ROOM);
			
			hp = 0;

			if (IS_SET(pExit->exit_info, EX_BASHPROOF))
			{
				act("The $d stands firm against your $T.", ch, pExit->keyword, skill_table[sn].noun_damage, TO_CHAR);
				act("The $d stands firm against $n's $T.", ch, pExit->keyword, skill_table[sn].noun_damage, TO_ROOM);
				pop_call();
				return TRUE;
			}				
			else if (IS_SET(pExit->exit_info, EX_IRON_DOOR))
			{
				hp = UMAX(hp, 60);
				hardness = 10;
			}
			else if (IS_SET(pExit->exit_info, EX_HEAVY_DOOR))
			{
				hp = UMAX(hp, 20);
				hardness = 5;
			}
			else if (IS_SET(pExit->exit_info, EX_WEAK_DOOR))
			{
				hp = UMAX(hp, 10);
				hardness = 5;
			}
			else
			{
				hp = UMAX(hp, 15);
				hardness = 5;
			}

			if (!IS_SET(dam_type, DAM_ACID|DAM_SONIC))
			{
				dam = UMAX(0, dam - hardness);
			}
			
			wiz_printf_room(ch, "Damage: %d vs. Hitpoints: %d\n\r", dam, hp);
			
			if (dam < hp)
			{
				act("The $d stands firm against your $t.", ch, skill_table[sn].noun_damage, pExit->keyword, TO_CHAR);
				act("The $d stands firm against $n's $t.", ch, skill_table[sn].noun_damage, pExit->keyword, TO_ROOM);
			}
			else
			{
				act("The $d is blasted open by your $t.", ch, skill_table[sn].noun_damage, pExit->keyword, TO_CHAR);
				act("The $d is blasted open by $n's $t.", ch, skill_table[sn].noun_damage, pExit->keyword, TO_ROOM);

				REMOVE_BIT(pExit->exit_info, EX_CLOSED);
				REMOVE_BIT(pExit->exit_info, EX_LOCKED);
				SET_BIT( pExit->exit_info, EX_BASHED );

				if ((pExit_rev = room_index[pExit->to_room]->exit[rev_dir[dir]]) != NULL
				&&  room_index[pExit_rev->to_room] == ch->in_room )
				{
					CHAR_DATA *rch;
	
					REMOVE_BIT(pExit_rev->exit_info, EX_CLOSED);
					REMOVE_BIT(pExit_rev->exit_info, EX_LOCKED);
					SET_BIT( pExit_rev->exit_info, EX_BASHED );
	
					for (rch = room_index[pExit->to_room]->first_person ; rch ; rch = rch->next_in_room)
					{
						act( "{118}BLAAAAM!!!{300}", rch, NULL, NULL, TO_CHAR);
						act( "The $d is blasted open from the other side!", rch, NULL, pExit_rev->keyword, TO_CHAR);
					}
				}
			}
		}
		pop_call();
		return TRUE;
	}	
	pop_call();
	return TRUE;
}


/*
 * Function used to recharge objects with 
 * storing properites in do_cast - Kregor
 */
bool recharge_obj( CHAR_DATA *ch, OBJ_DATA *obj, int sn )
{
	CHAR_DATA *rch, *rch_next;
	int val = 0;
	
	push_call("recharge_obj(%p,%p,%p)",ch,obj,sn);
	
	if (!is_spell(sn))
	{
		send_to_char("That is not a valid spell.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!IS_OBJ_TYPE(obj, ITEM_TREASURE) || !IS_SET(obj->value[0], TFLAG_SPELL_STORING|TFLAG_COUNTERSPELL))
	{
		if (!IS_OBJ_TYPE(obj, ITEM_STAFF))
		{
// 			act("$p cannot be recharged.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	}
	
	act("You cast a $T upon $p.", ch, obj, skill_table[sn].name, TO_CHAR);
	
	for (rch = ch->in_room->first_person ; rch != NULL ; rch = rch_next)
	{
		rch_next = rch->next_in_room;
		
		if (!can_see_casting(rch, ch, sn))
			continue;
		
		act( "$n casts a spell on $p.", ch, obj, rch, TO_VICT);
	}

	if (IS_OBJ_TYPE(obj, ITEM_STAFF))
	{
		if (obj->value[2] >= obj->value[1])
		{
			act("$p is already fully charged.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		for (val = 3 ; val <= 7 ; val++)
		{
			if (val == 7)
			{
				act("That spell is not present upon $p.", ch, obj, NULL, TO_CHAR);
				pop_call();
				return FALSE;
			}
			if (!is_spell(obj->value[val]) || obj->value[val] != sn)
				continue;

			obj->value[2] = UMIN(obj->value[2] + UMAX(1, skill_table[sn].native_level * 2 / 3), obj->value[1]);
			break;
		}
	}
	else
	{
		for (val = 1 ; val <= 3 ; val++)
		{
			if (is_spell(obj->value[val]))
			{
				if (val == 1 && TREASURE_FLAG(obj, TFLAG_COUNTERSPELL))
				{
					act("There is a spell already stored upon $p.", ch, obj, NULL, TO_CHAR);
					pop_call();
					return FALSE;
				}
				if (val == 3)
				{
					act("$p is already full of stored spells.", ch, obj, NULL, TO_CHAR);
					pop_call();
					return FALSE;
				}					
				continue;
			}
			obj->value[val] = sn;
			break;
		}				
	}
	act("{138}$p glows briefly.", ch, obj, NULL, TO_ALL);
		
	pop_call();
	return TRUE;
}


/*
	Apply deity favor for using spells attuned to deity's domains - Kregor
*/
void spell_favor(CHAR_DATA *ch, int sn)
{
	push_call("spell_favor(%p,%p)",ch,sn);
	
	if (IS_SET(skill_table[sn].spell_desc, SDESC_AIR))
		gain_favor(ch, DOMAIN_AIR, 1);
	if (IS_SET(skill_table[sn].spell_desc, SDESC_CHAOTIC))
		gain_favor(ch, DOMAIN_CHAOS, 1);
	if (IS_SET(skill_table[sn].spell_desc, SDESC_DARKNESS))
		gain_favor(ch, DOMAIN_DARKNESS, 1);
	if (IS_SET(skill_table[sn].spell_desc, SDESC_DEATH|SDESC_DISEASE|SDESC_POISON))
	{
		gain_favor(ch, DOMAIN_DEATH, 1);
		gain_favor(ch, DOMAIN_DESTRUCTION, 1);
	}
	if (IS_SET(skill_table[sn].spell_desc, SDESC_EARTH))
		gain_favor(ch, DOMAIN_EARTH, 1);
	if (IS_SET(skill_table[sn].spell_desc, SDESC_EVIL))
		gain_favor(ch, DOMAIN_EVIL, 1);
	if (IS_SET(skill_table[sn].spell_desc, SDESC_FIRE))
	{
		gain_favor(ch, DOMAIN_FIRE, 1);
		gain_favor(ch, DOMAIN_SUN, 1);
	}
	if (IS_SET(skill_table[sn].spell_desc, SDESC_GOOD))
		gain_favor(ch, DOMAIN_GOOD, 1);
	if (IS_SET(skill_table[sn].spell_desc, SDESC_HEALING))
	{
		gain_favor(ch, DOMAIN_HEALING, 1);
		gain_favor(ch, DOMAIN_RENEWAL, 1);
	}
	if (IS_SET(skill_table[sn].spell_desc, SDESC_ILLUSION))
		gain_favor(ch, DOMAIN_ILLUSION, 1);
	if (IS_SET(skill_table[sn].spell_desc, SDESC_LAWFUL))
		gain_favor(ch, DOMAIN_LAW, 1);
	if (IS_SET(skill_table[sn].spell_desc, SDESC_LIGHT))
	{
		gain_favor(ch, DOMAIN_MOON, 1);
		gain_favor(ch, DOMAIN_SUN, 1);
	}
	if (IS_SET(skill_table[sn].spell_desc, SDESC_MIND))
		gain_favor(ch, DOMAIN_CHARM, 1);
	if (IS_SET(skill_table[sn].spell_desc, SDESC_NEGATIVE))
	{
		gain_favor(ch, DOMAIN_DEATH, 1);
		gain_favor(ch, DOMAIN_SUFFERING, 1);
	}
	if (IS_SET(skill_table[sn].spell_desc, SDESC_WATER))
		gain_favor(ch, DOMAIN_WATER, 1);
		
	if (skill_table[sn].spell_school == SCHOOL_ABJURATION)
	{
		gain_favor(ch, DOMAIN_PROTECTION, 1);
		gain_favor(ch, DOMAIN_COMMUNITY, 1);
		gain_favor(ch, DOMAIN_PLANNING, 1);
		gain_favor(ch, DOMAIN_LIBERATION, 1);
	}
	if (skill_table[sn].spell_school == SCHOOL_EVOCATION)
	{
		gain_favor(ch, DOMAIN_DESTRUCTION, 1);
		gain_favor(ch, DOMAIN_WRATH, 1);
		gain_favor(ch, DOMAIN_WAR, 1);
	}
	if (skill_table[sn].spell_school == SCHOOL_ENCHANTMENT)
	{
		gain_favor(ch, DOMAIN_CHARM, 1);
		gain_favor(ch, DOMAIN_MADNESS, 1);
	}
	if (skill_table[sn].spell_school == SCHOOL_ILLUSION)
	{
		gain_favor(ch, DOMAIN_ILLUSION, 1);
		gain_favor(ch, DOMAIN_MADNESS, 1);
		gain_favor(ch, DOMAIN_TRICKERY, 1);
	}
	if (skill_table[sn].spell_school == SCHOOL_NECROMANCY)
	{
		gain_favor(ch, DOMAIN_DEATH, 1);
		gain_favor(ch, DOMAIN_REPOSE, 1);
	}
	if (skill_table[sn].spell_school == SCHOOL_DIVINATION)
	{
		gain_favor(ch, DOMAIN_KNOWLEDGE, 1);
		gain_favor(ch, DOMAIN_PLANNING, 1);
	}
	
	gain_favor(ch, DOMAIN_MAGIC, 1);
	
	pop_call();
	return;
}


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


/*
 * Thoroughly gutted do_cast to put the casting time on a spell.
 * Now cues the spell and counts down the release of the spell.
 * Also separated main function for use both as a racial cast
 * and a normal cast with mana - Kregor
 */
bool cast_spell( CHAR_DATA *ch, char *argument, bool fRacecast )
{
	char arg1[MAX_INPUT_LENGTH];
	char arg2[MAX_INPUT_LENGTH];
	char arg3[MAX_INPUT_LENGTH];
	char adj[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	CHAR_DATA *rch, *rch_next;
	OBJ_DATA *obj, *symbol;
	void *vo;
	bool ranged = FALSE;
	bool ProtTouch = FALSE;
	int mana, sn, level, target, dir, timer, range, class, stat, feats, circle, favor;

	push_call("cast_spell(%p,%p)",ch,argument);

	if (ch->casting || ch->cast_timer > 0)
	{	
		ch_printf_color(ch, "You are already casting %s. Use STOP to stop casting\n\r.", skill_table[ch->cast_sn].name);
		pop_call();
		return FALSE;
	}

	if (in_combat(ch) && !is_active(ch))
	{	
		ch_printf_color(ch, "Just wait your turn!\n\r");
		pop_call();
		return FALSE;
	}

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

	if (*arg1 == '\0')
	{
		if (fRacecast)
			send_to_char( "Syntax: racecast <spell> [direction] [<target>]\n\r", ch );
		else
			send_to_char( "Syntax: cast <spell> [direction] [<target>]\n\r", ch );
		pop_call();
		return FALSE;
	}

	sn = skill_lookup( arg1 );

	if (sn < 0 || !is_spell(sn))
	{
		if (!IS_NPC(ch) && ch->desc == NULL)
		{
			log_printf("[%u] casting unknown spell: %s", ch->pIndexData->vnum, arg1);
		}
		send_to_char( "That is not a spell.\n\r", ch );
		pop_call();
		return FALSE;
	}
	
	// Not sure why it wasn't done this way to begin with - Kregor
	if (!skill_table[sn].spell_fun || skill_table[sn].spell_fun == spell_null)
	{	
		ch_printf_color(ch, "OOC: That spell is not completed yet.\n\r");
		pop_call();
		return FALSE;
	}

	if (IS_SET(ch->action, ACTION_STANDARD) && skill_table[sn].beats >= CASTING_STANDARD)
	{	
		ch_printf_color(ch, "You have already made a standard action this round.\n\r");
		pop_call();
		return FALSE;
	}
	if (IS_SET(ch->action, ACTION_SWIFT) && skill_table[sn].beats == CASTING_SWIFT)
	{	
		ch_printf_color(ch, "You have already made a swift action this round.\n\r");
		pop_call();
		return FALSE;
	}
	
	if (fRacecast)
	{
		if (!race_skill(ch, sn))
		{
			send_to_char("You cannot use that as a spell-like ability.\n\r", ch);
			pop_call();
			return FALSE;
		}
		else
		{
			level = ch->level;
		}
	}
	else
	{
		if ((level = get_caster_level(ch, sn)) == -1)
		{
			pop_call();
			return FALSE;
		}
	}
	
	if (is_safe_magic(ch, NULL, sn))
	{
		pop_call();
		return FALSE;
	}

	/*
	 * Determine whether it's a ranged cast or not - Kregor 6/29/07
	 */
	if (*arg2 != '\0'
	&& skill_table[sn].target != TAR_IGNORE
	&& skill_table[sn].target != TAR_CHAR_SELF
	&& skill_table[sn].target != TAR_OBJ_INV
	&& skill_table[sn].target != TAR_OBJ_ROOM
	&& skill_table[sn].target != TAR_OBJ_WIELD)
	{
		if ((dir = direction_door(arg2)) != -1)
		{
			ranged = TRUE;
			target_name = argument;
			argument    = one_argument( argument, arg2 );
			argument    = one_argument( argument, arg3 );
		}
	}
	else
	{
		argument    = one_argument( argument, arg3 );
	}

	if (ranged)
	{
		if (!get_exit(ch->in_room->vnum, dir)
		|| (is_string(arg2) && !is_valid_exit(ch, ch->in_room, dir)))
		{
			send_to_char( "You cannot cast in that direction.\n\r", ch );
			pop_call();
			return FALSE;
		}
		if ((range = get_spell_range(level, sn)) <= 0)
		{	
			ch_printf_color(ch, "That spell cannot be cast at that range.\n\r");
			pop_call();
			return FALSE;
		}
	}	

	if (!fRacecast)
	{
		if ((circle = get_spell_circle(ch, sn)) < 0)
		{
			send_to_char("You cannot cast that spell.\n\r", ch);
			if (IS_NPC(ch))
				log_build_printf(ch->pIndexData->vnum, "do_cast: mobile gets -1 on get_spell_circle");
			pop_call();
			return FALSE;
		}	
	
		if (IS_NPC(ch))
		{
			class = multi(ch, sn);
		}
		else
		{
			if ((class = prepared(ch, sn)) == -1)
			{
				if ((class = spontaneous_cast(ch, sn)) == -1)
				{
					send_to_char("You have not prepared that spell.\n\r", ch);
					pop_call();
					return FALSE;
				}
			}
		}
				
		if (!IS_SET(learned(ch, sn), 1 << class))
		{
			send_to_char("You do not know that spell.\n\r", ch);
			pop_call();
			return FALSE;
		}
		
		if (class == CLASS_DRUID && wears_metal(ch, FALSE))
		{
			send_to_char("You have displeased nature by wearing metal.\n\r", ch);
			pop_call();
			return FALSE;
		}
			
		switch (class_table[class].attr_prime)
		{
			case APPLY_WIS:
				stat = get_curr_wis(ch);
				sprintf(adj, "wise");
				break;
			case APPLY_INT:
				stat = get_curr_int(ch);
				sprintf(adj, "intelligent");
				break;
			case APPLY_CHA:
				stat = get_curr_cha(ch);
				sprintf(adj, "charismatic");
				break;
		}
		if (circle > stat - 10)
		{
			ch_printf_color(ch, "You are not %s enough to cast that spell.\n\r", adj);
			if (IS_NPC(ch))
				log_build_printf(ch->pIndexData->vnum, "cast_spell: mobile stat too low to cast");
			pop_call();
			return FALSE;
		}	
	
		if (!IS_NPC(ch) && !IS_GOD(ch) && class_table[class].attr_prime == APPLY_WIS)
		{
			if (IS_SET(skill_table[sn].flags, SF_DIVINEFOCUS))
			{
				if (ch->god <= GOD_NEUTRAL)
				{	
					ch_printf_color(ch, "You have to be devoted to a deity to cast that!\n\r");
					pop_call();
					return FALSE;
				}
				if ((symbol = get_obj_wear_type(ch, ITEM_SYMBOL)) == NULL)
				{
					send_to_char( "You don't have your holy symbol.\n\r", ch);
					pop_call();
					return FALSE;
				}		
				if (symbol->value[2] != ch->god)
				{
					send_to_char( "You need to be carrying your OWN deity's symbol!\n\r", ch);
					pop_call();
					return FALSE;
				}
			}
			// Added check and cost of favor for divine spells - Kregor 10/30/13
			if (!IS_NPC(ch) && !IS_GOD(ch))
			{
				switch (circle)
				{
					default:
						favor = 0;
						break;
					case 7:
						favor = 10;
						break;
					case 8:
						favor = 20;
						break;
					case 9:
						favor = 30;
						break;
				}
				if (ch->pcdata->god_favor < favor)
				{
					send_to_char( "You lack enough favor to cast that!\n\r", ch);
					pop_call();
					return FALSE;
				}
			}
		}
	
		// process & adjust metamagic before checking set feats.
		wiz_printf_room(ch, "cast_spell: metamagic feats set: %s\n\r", ch->metamagic == 0 ? "none" : flag_string(ch->metamagic, metamagic_flags));
		feats = get_metamagic(ch, sn, ch->metamagic, circle, class, TRUE);
// 		circle = get_metamagic(ch, sn, ch->metamagic, circle, class, FALSE);
		mana = get_mana(ch, sn, circle, class);
		wiz_printf_room(ch, "cast_spell: metamagic feats set: %s\n\r", feats == 0 ? "none" : flag_string(feats, metamagic_flags));
	
		if (ch->mana[class] < mana)
		{
			ch_printf(ch, "That would take %d mana; you only have %d to expend.\n\r", mana, ch->mana[class]);
			pop_call();
			return FALSE;
		}
		
		if (IS_SET(skill_table[sn].flags, SF_SOMATIC)
		&& (hands_full(ch) || !is_handy(ch) || IS_AFFECTED(ch, AFF_GASEOUS))
		&& !IS_SET(feats, METAMAGIC_STILL))
		{
			ch_printf_color(ch, "You have no free hand to cast with.\n\r");
			if (IS_NPC(ch))
				log_build_printf(ch->pIndexData->vnum, "cast_spell: mobile has no free hand to cast");
			pop_call();
			return FALSE;
		}

		if (IS_SET(skill_table[sn].flags, SF_VERBAL) && IS_AFFECTED(ch, AFF2_SILENCE) && !IS_SET(feats, METAMAGIC_SILENT))
		{
			send_to_char("You cannot seem to muster the words.\n\r", ch);
			pop_call();
			return FALSE;
		}
		
		if (ranged && IS_SET(feats, METAMAGIC_ENLARGE))
		{
			range *= 2;
		}

		if (!check_components(ch, sn, FALSE))
		{
			pop_call();
			return FALSE;
		}
	}
	else
	{
		if (IS_PLR(ch, PLR_WIZTIME))
			ch_printf_color(ch, "{058}DEBUG: uses for %s: %d\n\r", skill_table[sn].name, ch->uses[sn]);
	
		if (ch->uses[sn] >= race_skill(ch, sn))
		{
			send_to_char("You cannot use that ability again without resting.\n\r", ch);
			pop_call();
			return FALSE;
		}
		// race casting uses CHA to work, uses native spell level - Kregor
		if (get_spell_circle(NULL, sn) > 10 + stat_bonus(TRUE, ch, APPLY_CHA))
		{
			send_to_char("You are not charismatic enough to use that spell.\n\r", ch);
			pop_call();
			return FALSE;
		}
		class = -2;
	}

	/*
		Consider conditional limitations of spells - Kregor
	*/
	if (IS_SET(skill_table[sn].spell_desc, SDESC_FIRE|SDESC_AIR) && ch->in_room->sector_type == SECT_UNDER_WATER)
	{
		act("You cannot cast that spell underwater!", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	/*
		Locate targets.
	*/
	victim	= NULL;
	obj		= NULL;
	vo		= NULL;
	target	= skill_table[sn].target;
	
	/*
	 * look for spell storing object cast upon first - Kregor
	 */
	if (!IS_NPC(ch) && !fRacecast)
	{
		if (*arg2 != '\0' && (obj = get_obj_list(ch, arg2, ch->first_carrying)) != NULL)
		{
			if (recharge_obj(ch, obj, sn))
			{
				ch->mana[class] -= mana;

				if (!IS_SET(feats, METAMAGIC_QUICKEN) && skill_table[sn].beats != CASTING_SWIFT)
				{
					if (skill_table[sn].beats == CASTING_STANDARD)
						TAKE_ACTION(ch, ACTION_STANDARD);
					else if (skill_table[sn].beats == CASTING_ROUND)
						TAKE_ACTION(ch, ACTION_FULL);
				}
				pop_call();
				return TRUE;
			}
		}
	}

	if (ranged)
	{
		switch (target)
		{
			default:
				send_to_char( "That spell cannot be cast into another room!\n\r", ch );
				pop_call();
				return FALSE;
			case TAR_CHAR_OFFENSIVE:
			case TAR_CHAR_DEFENSIVE:
			case TAR_UNDEAD_OFF:
			case TAR_UNDEAD_DEF:
			case TAR_OBJ_CHAR_OFF:
			case TAR_OBJ_CHAR_DEF:
				break;
		}
	}

	if (ranged && !is_string(arg2))
	{
		if (!check_cast_door(ch, dir, sn, level, FALSE))
		{
			pop_call();
			return FALSE;
		}
	}
	else
	{
		switch (target)
		{
			default:
				bug( "cast_spell: bad target for sn %d.", sn );
				pop_call();
				return FALSE;
	
			case TAR_IGNORE:
				break;
	
			case TAR_CHAR_OFFENSIVE:
				if (arg2[0] == '\0')
				{
					if (who_fighting(ch) == NULL)
					{
						send_to_char( "Cast the spell on whom?\n\r", ch );
						pop_call();
						return FALSE;
					}
					victim = who_fighting(ch);
				}
				else
				{
					if (ranged)
					{
						if ((victim = find_char_dir(ch, dir, arg2)) == NULL)
						{
							send_to_char("Your target must have slipped out of sight!\n\r", ch);
							pop_call();
							return FALSE;
						}
					}
					else if ((victim = get_char_room(ch, arg2)) == NULL)
					{
						send_to_char( "Your target isn't here.\n\r", ch );
						pop_call();
						return FALSE;
					}
				}
				if (is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return FALSE;
				}
				vo = (void *) victim;
				break;
	
			case TAR_UNDEAD_OFF:
				if (arg2[0] == '\0')
				{
					if ((victim = who_fighting(ch)) == NULL || !IS_UNDEAD(who_fighting(ch)))
					{
						if (IS_UNDEAD(ch))
						{
							send_to_char( "Cast the spell on whom?\n\r", ch );
							pop_call();
							return FALSE;
						}
						else
						{
							victim = ch;
						}
					}
				}
				else
				{
					if (ranged)
					{
						if ((victim = find_char_dir(ch, dir, arg2)) == NULL)
						{
							send_to_char("Your target must have slipped out of sight!\n\r", ch);
							pop_call();
							return FALSE;
						}
					}
					else if ((victim = get_char_room(ch, arg2)) == NULL)
					{
						send_to_char( "Your target isn't here.\n\r", ch );
						pop_call();
						return FALSE;
					}
				}
				if (IS_UNDEAD(victim) && is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return FALSE;
				}
				vo = (void *) victim;
				if (IS_UNDEAD(victim))
					target = TAR_CHAR_OFFENSIVE;
				else
					target = TAR_CHAR_DEFENSIVE;
				break;
	
			case TAR_CHAR_DEFENSIVE:
				if (arg2[0] == '\0')
				{
					victim = ch;
				}
				else
				{
					if (ranged)
					{
						if ((victim = find_char_dir(ch, dir, arg2)) == NULL)
						{
							send_to_char("Your target must have slipped out of sight!\n\r", ch);
							pop_call();
							return FALSE;
						}
					}
					else if ((victim = get_char_room(ch, arg2)) == NULL)
					{
						send_to_char( "Your target isn't here.\n\r", ch );
						pop_call();
						return FALSE;
					}
				}
				if (is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return FALSE;
				}
				vo = (void *) victim;
				break;
	
			case TAR_UNDEAD_DEF:
				if (arg2[0] == '\0')
				{
					if ((victim = who_fighting(ch)) == NULL || IS_UNDEAD(who_fighting(ch)))
					{
						if (!IS_UNDEAD(ch))
						{
							send_to_char( "Cast the spell on whom?\n\r", ch );
							pop_call();
							return FALSE;
						}
						else
						{
							victim = ch;
						}
					}
				}
				else
				{
					if (ranged)
					{
						if ((victim = find_char_dir(ch, dir, arg2)) == NULL)
						{
							send_to_char("Your target must have slipped out of sight!\n\r", ch);
							pop_call();
							return FALSE;
						}
					}
					else if ((victim = get_char_room(ch, arg2)) == NULL)
					{
						send_to_char( "Your target isn't here.\n\r", ch );
						pop_call();
						return FALSE;
					}
				}
				if (!IS_UNDEAD(victim) && is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return FALSE;
				}
				vo = (void *) victim;
				if (!IS_UNDEAD(victim))
					target = TAR_CHAR_OFFENSIVE;
				else
					target = TAR_CHAR_DEFENSIVE;
				break;
	
			case TAR_CHAR_SELF:
				if (ranged)
				{
					send_to_char( "You cannot cast this spell at range.\n\r", ch );
					pop_call();
					return FALSE;
				}
				if (arg2[0] == '\0' || is_name_short(arg2, ch->name))
				{
					victim = ch;
				}
				else
				{
					if (!domain_skill(ch, gsn_protective_touch))
					{
						send_to_char( "You can only cast this spell upon yourself.\n\r", ch );
						pop_call();
						return FALSE;
					}
					else if (skill_table[sn].spell_school != SCHOOL_ABJURATION)
					{
						send_to_char( "You cannot cast that spell upon others.\n\r", ch );
						pop_call();
						return FALSE;
					}
					else if ((victim = get_char_room(ch, arg2)) == NULL)
					{
						send_to_char( "Your target isn't here.\n\r", ch );
						pop_call();
						return FALSE;
					}
					ProtTouch = TRUE;
				}
				if (ch != victim && is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return FALSE;
				}			
				vo = (void *) victim;
				break;
	
			case TAR_OBJ_INV:
				if (arg2[0] == '\0')
				{
					send_to_char( "What should the spell be cast upon?\n\r", ch );
					pop_call();
					return FALSE;
				}
				if ((obj = get_obj_carry(ch, arg2)) == NULL)
				{
					if ((obj = get_obj_wear(ch, arg2)) == NULL)
					{
						send_to_char( "You are not carrying that.\n\r", ch );
						pop_call();
						return FALSE;
					}
				}
				vo = (void *) obj;
				break;
	
			case TAR_OBJ_WIELD:
				if (arg2[0] == '\0')
				{
					if ((obj = get_wield(ch, FALSE)) == NULL)
					{
						send_to_char( "What should the spell be cast upon?\n\r", ch );
						pop_call();
						return FALSE;
					}
				}
				if ((obj = get_obj_wear(ch, arg2)) == NULL)
				{
					send_to_char( "You are not holding that.\n\r", ch );
					pop_call();
					return FALSE;
				}
				vo = (void *) obj;
				break;
	
			case TAR_OBJ_ROOM:
				if (arg2[0] == '\0')
				{
					send_to_char( "What should the spell be cast upon?\n\r", ch );
					pop_call();
					return FALSE;
				}
				if ((obj = get_obj_here(ch, arg2)) == NULL)
				{
					send_to_char( "That item does not seem to be here.\n\r", ch );
					pop_call();
					return FALSE;
				}
				vo = (void *) obj;
				break;
	
			case TAR_OBJ_CHAR_DEF:
				if (arg2[0] == '\0')
				{
					victim = ch;
				}
				else if ((victim = get_char_room(ch, arg2)) == NULL)
				{
					if ((obj = get_obj_carry(ch, arg2)) == NULL)
					{
						send_to_char( "Who or what should the spell be cast upon?\n\r", ch );
						pop_call();
						return FALSE;
					}
				}
				if (victim)
				{
					if (is_safe_magic(ch, victim, sn))
					{
						pop_call();
						return FALSE;
					}
					target = TAR_CHAR_DEFENSIVE;
					vo     = (void *) victim;
				}
				else
				{
					if (ranged)
					{
						send_to_char( "That item does not seem to be here.\n\r", ch );
						pop_call();
						return FALSE;
					}
					target = TAR_OBJ_INV;
					vo     = (void *) obj;
				}
				break;
	
			case TAR_OBJ_CHAR_OFF:
	
				if (arg2[0] == '\0')
				{
					if (who_fighting(ch) == NULL)
					{
						send_to_char("Cast the spell on whom?\n\r", ch);
						pop_call();
						return FALSE;
					}
					victim = who_fighting(ch);
				}
				else if ((victim = get_char_room(ch, arg2)) == NULL)
				{
					if ((obj = get_obj_carry(ch, arg2)) == NULL)
					{
						send_to_char( "Who or what should the spell be cast upon?\n\r", ch );
						pop_call();
						return FALSE;
					}
				}
	
				if (victim)
				{
					if (is_safe_magic(ch, victim, sn))
					{
						pop_call();
						return FALSE;
					}
					target = TAR_CHAR_OFFENSIVE;
					vo     = (void *) victim;
				}
				else
				{
					if (ranged)
					{
						send_to_char( "That item does not seem to be here.\n\r", ch );
						pop_call();
						return FALSE;
					}
					target = TAR_OBJ_INV;
					vo     = (void *) obj;
				}
				break;
		}
		
		if (IS_SET(skill_table[sn].flags, SF_TOUCH))
		{
			if (victim == NULL && obj == NULL)
			{
				act("There's noone to touch.\n\r", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return FALSE;
			}
			if ((IS_INCORPOREAL(ch) && !IS_INCORPOREAL(victim))
			|| (!IS_INCORPOREAL(ch) && IS_INCORPOREAL(victim)))
			{
				act("You cannot touch $M them to cast that.\n\r", ch, NULL, victim, TO_CHAR);
				pop_call();
				return FALSE;
			}
		}
	}
		
	wiz_printf("cast stage 1...");

	// allow pre-cast checking of spell_fun conditions
	ForReal = FALSE;
	argument = target_name; // cache back target_name for testing spell_fun
	
	if (((*skill_table[sn].spell_fun) (sn, level, ch, vo, target)) == FALSE)
	{
		pop_call();
		return FALSE;
	}
	
	BOOL_CHECK_TURN(ch, victim);

	if (ranged && victim && findpath_search_victim(ch, victim, range) == -1)
	{
		send_to_char("That target is out of range of your spell.\n\r", ch);
		pop_call();
		return FALSE;
	}

	timer = skill_table[sn].beats;
	
	if (victim)
	{
		if (target == TAR_CHAR_OFFENSIVE
		|| (target == TAR_UNDEAD_OFF && IS_UNDEAD(victim))
		|| (target == TAR_UNDEAD_DEF && !IS_UNDEAD(victim)))
		{
			if (!check_murder(ch, victim))
			{
				pop_call();
				return FALSE;
			}
		}
	}
	
	ch->casting 			= TRUE;
	ch->cast_timer		= 0;
	ch->casting_time	= timer;
	ch->cast_sn				= sn;
	ch->cast_level		= level;
	ch->cast_class		= class;
	ch->cast_dir			= dir;
	ch->cast_feats		= fRacecast ? 0 : feats;
	ch->cast_circle		=	fRacecast ? -2 : circle;
	// because argument cached original target_name before spell_fun check
	ch->target_name 	= STRALLOC(argument);
	
	if (fRacecast)
	{
		act( "You begin to use a spell-like ability.", ch, NULL, NULL, TO_CHAR);
		act( "$n starts to concentrate.", ch, NULL, NULL, TO_ROOM);
	}
	else if (class == CLASS_BARD)
	{
		act( "$n begins to sing...", ch, NULL, NULL, TO_ROOM);
		act( "You begin to sing...", ch, NULL, NULL, TO_CHAR);
	}	
	else
	{
		act( "You begin to cast a spell.", ch, NULL, NULL, TO_CHAR);
		for (rch = ch->in_room->first_person ; rch != NULL ; rch = rch_next)
		{
			rch_next = rch->next_in_room;
			
			if (!can_see_casting(rch, ch, sn))
				continue;
			
			act( "$n begins to cast a spell.", ch, NULL, rch, TO_VICT);
		}
	}

	if (target == TAR_CHAR_OFFENSIVE
	|| (target == TAR_UNDEAD_OFF && IS_UNDEAD(victim))
	|| (target == TAR_UNDEAD_DEF && !IS_UNDEAD(victim)))
	{
		if (can_see_casting(victim, ch, sn))
			make_char_fight_char( ch, victim );
	}
	
	if (fRacecast)
		TAKE_ACTION(ch, ACTION_STANDARD);
	else if (IS_SET(feats, METAMAGIC_QUICKEN) || skill_table[sn].beats == CASTING_SWIFT)
	{
		TAKE_ACTION(ch, ACTION_SWIFT);
		release_cast(ch);
	}
	else if (skill_table[sn].beats == CASTING_STANDARD)
		TAKE_ACTION(ch, ACTION_STANDARD);
	else if (skill_table[sn].beats == CASTING_ROUND)
		TAKE_ACTION(ch, ACTION_FULL);
	else if (skill_table[sn].beats == CASTING_INSTANT)
		release_cast(ch);

	pop_call();
	return TRUE;
}

 
void do_cast( CHAR_DATA *ch, char *argument )
{
	push_call("do_cast(%p,%p)",ch,argument);

	cast_spell(ch, argument, FALSE);
	
	pop_call();
	return;
}


void do_racecast( CHAR_DATA *ch, char *argument )
{
	push_call("do_racecast(%p,%p)",ch,argument);

	cast_spell(ch, argument, TRUE);
	
	pop_call();
	return;
}


/* The spell's release after the countdown - Kregor */
void release_cast( CHAR_DATA *ch )
{
	char arg2[MAX_INPUT_LENGTH];
	char arg3[MAX_INPUT_LENGTH];
	char *argument;
	CHAR_DATA *victim;
	OBJ_DATA *obj, *vic_obj;
	void *vo;
	bool ranged = FALSE;
	bool race = FALSE;
	bool Turned = FALSE;
	bool Fail = FALSE; // means the spell spoiled, but still costs.
	int mana, sn, level, target, dir, diceroll, class;

	push_call("release_cast(%p)",ch);

	target_name = STRALLOC(ch->target_name);
	argument = STRALLOC(ch->target_name);

	if ((sn = ch->cast_sn) < 0 || !is_spell(sn))
	{
		bug( "release_cast(%s): %d not a spell.", ch->name, sn );
		pop_call();
		return;
	}

	if ((level = ch->cast_level) == -1)
	{
		if ((level = get_caster_level(ch, sn)) == -1)
		{
			pop_call();
			return;
		}
	}
	
	if (ch->cast_circle == -2)
	{
		if (!race_skill(ch, sn))
		{
			bug( "release_cast(%s): race cast of non-racial ability.", ch->name );
			pop_call();
			return;
		}
		ch->cast_circle = ch->level / 2;
		race = TRUE;
	}

	if ((dir = ch->cast_dir) != -1)
	{
		ranged = TRUE;
	}
		
	argument    = one_argument( argument, arg2 );
	argument    = one_argument( argument, arg3 );

	value = -1;   /* Default value */

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

	if (is_safe_magic(ch, NULL, sn))
	{
		pop_call();
		return;
	}

	if (!race)
	{
		if ((class = casting_class(ch, sn, TRUE)) == -1)
		{
			send_to_char("Wait a minute! You can't cast this spell!\n\r", ch);
			bug ("release_cast: class = -1", 0);
			pop_call();
			return;
		}
		
		mana = get_mana(ch, sn, ch->cast_circle, class);
	
		if (!IS_SET(ch->cast_feats, METAMAGIC_STILL)
		&& IS_SET(skill_table[sn].flags, SF_SOMATIC))
		{
			switch (class)
			{
				default:
					break;
				case CLASS_WIZARD:
				case CLASS_SORCERER:
				case CLASS_BARD:
					if (number_percent() < spell_failure(ch))
					{
						send_to_char_color("{138}Your encumberance has spoiled your casting!\n\r", ch);
						Fail = TRUE;
						break;
					}
					break;
			}
		}
	}
	
	if (!Fail)
	{
		diceroll = concentration_roll(ch);
	
		if (IS_ENTANGLED(ch) || drunk_level(ch) >= DRUNK_TIPSY || is_affected(ch, gsn_insect_plague) || ch->grappling || ch->grappled_by)
		{
			int DC;
			
			if (ch->grappled_by)
				DC = 10 + combat_maneuver_bonus(ch->grappled_by) + ch->cast_circle;
			else
				DC = 15 + ch->cast_circle;
				
			if (!concentration_check(ch, NULL, diceroll, DC))
			{
				send_to_char_color("{138}You lost your concentration!\n\r", ch);
				Fail = TRUE;
			}
		}
	}
	
	// Here is where NO_ASTRAL flags spoil summons and teleports
	if (IS_SET(ch->in_room->room_flags, ROOM_NO_ASTRAL))
	{
		if (!Fail && IS_SET(skill_table[sn].spell_desc, SDESC_SUMMONING))
		{
			act("Magical wards foil your summoning.", ch, NULL, NULL, TO_CHAR);
			Fail = TRUE;
		}
	}

	// Locate targets again, JUST in case something gets away! ;)
	// but bypass if Fail already, because it doesn't matter.
	victim	= NULL;
	obj			= NULL;
	vo			= NULL;
	target	= skill_table[sn].target;

	if (!Fail)
	{
		if (ranged && !is_string(arg2) && check_cast_door(ch, dir, sn, level, TRUE))
		{
			if (!race)
			{
				ch->mana[class] -= mana;
				check_components(ch, sn, TRUE);
			}
			else
			{
				ch->uses[sn]++;
			}
			pop_call();
			return;
		}
		switch (target)
		{
			default:
				bug( "release_cast: bad target for sn %d.", sn );
				pop_call();
				return;
	
			case TAR_IGNORE:
				break;
	
			case TAR_CHAR_OFFENSIVE:
				if (arg2[0] == '\0')
				{
					if (who_fighting(ch) == NULL)
					{
						send_to_char( "Your target must have slipped away.\n\r", ch );
						pop_call();
						return;
					}
					victim = who_fighting(ch);
				}
				else
				{
					if (ranged)
					{
						if ((victim = find_char_dir(ch, dir, arg2)) == NULL)
						{
							send_to_char("Your target must have slipped out of sight!\n\r", ch);
							pop_call();
							return;
						}
					}
					else if ((victim = get_char_room(ch, arg2)) == NULL)
					{
						send_to_char( "Your target must have slipped away.\n\r", ch );
						pop_call();
						return;
					}
				}
				if (is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return;
				}
				vo = (void *) victim;
				break;
	
			case TAR_UNDEAD_OFF:
				if (arg2[0] == '\0')
				{
					if ((victim = who_fighting(ch)) == NULL || !IS_UNDEAD(who_fighting(ch)))
					{
						if (IS_UNDEAD(ch))
						{
							send_to_char( "Your target must have slipped away.\n\r", ch );
							pop_call();
							return;
						}
						else
						{
							victim = ch;
						}
					}
				}
				else
				{
					if (ranged)
					{
						if ((victim = find_char_dir(ch, dir, arg2)) == NULL)
						{
							send_to_char("Your target must have slipped out of sight!\n\r", ch);
							pop_call();
							return;
						}
					}
					else if ((victim = get_char_room(ch, arg2)) == NULL)
					{
						send_to_char( "Your target must have slipped away.\n\r", ch );
						pop_call();
						return;
					}
				}
				if (IS_UNDEAD(victim) && is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return;
				}
				vo = (void *) victim;
				if (IS_UNDEAD(victim))
					target = TAR_CHAR_OFFENSIVE;
				else
					target = TAR_CHAR_DEFENSIVE;
				break;
	
			case TAR_UNDEAD_DEF:
				if (arg2[0] == '\0')
				{
					if ((victim = who_fighting(ch)) == NULL || IS_UNDEAD(who_fighting(ch)))
					{
						if (!IS_UNDEAD(ch))
						{
							send_to_char("Your target must have slipped out of sight!\n\r", ch);
							pop_call();
							return;
						}
						else
						{
							victim = ch;
						}
					}
				}
				else
				{
					if (ranged)
					{
						if ((victim = find_char_dir(ch, dir, arg2)) == NULL)
						{
							send_to_char("Your target must have slipped out of sight!\n\r", ch);
							pop_call();
							return;
						}
					}
					else if ((victim = get_char_room(ch, arg2)) == NULL)
					{
						send_to_char( "Your target must have slipped away.\n\r", ch );
						pop_call();
						return;
					}
				}
				if (!IS_UNDEAD(victim) && is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return;
				}
				vo = (void *) victim;
				if (!IS_UNDEAD(victim))
					target = TAR_CHAR_OFFENSIVE;
				else
					target = TAR_CHAR_DEFENSIVE;
				break;
	
			case TAR_CHAR_DEFENSIVE:
				if (arg2[0] == '\0')
				{
					victim = ch;
				}
				else
				{
					if (ranged)
					{
						if ((victim = find_char_dir(ch, dir, arg2)) == NULL)
						{
							send_to_char("Your target must have slipped out of sight!\n\r", ch);
							pop_call();
							return;
						}
					}
					else if ((victim = get_char_room(ch, arg2)) == NULL)
					{
						send_to_char( "Your target must have slipped away.\n\r", ch );
						pop_call();
						return;
					}
				}
				if (is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return;
				}
				vo = (void *) victim;
				break;
	
			case TAR_CHAR_SELF:
				vo = (void *) ch;
				break;
	
			case TAR_OBJ_INV:
				if (arg2[0] == '\0')
				{
					send_to_char( "Something must have gone wrong.\n\r", ch );
					pop_call();
					return;
				}
				if ((obj = get_obj_carry(ch, arg2)) == NULL)
				{
					if ((obj = get_obj_wear(ch, arg2)) == NULL)
					{
						send_to_char( "You are not carrying that.\n\r", ch );
						pop_call();
						return;
					}
				}
				vo = (void *) obj;
				break;
	
			case TAR_OBJ_WIELD:
				if (arg2[0] == '\0')
				{
					if ((obj = get_wield(ch, FALSE)) == NULL)
					{
						send_to_char( "Something must have gone wrong.\n\r", ch );
						pop_call();
						return;
					}
				}
				if ((obj = get_obj_wear(ch, arg2)) == NULL)
				{
					send_to_char( "You are not holding that.\n\r", ch );
					pop_call();
					return;
				}
				vo = (void *) obj;
				break;
	
			case TAR_OBJ_ROOM:
				if (arg2[0] == '\0')
				{
					send_to_char( "Something must have gone wrong.\n\r", ch );
					pop_call();
					return;
				}
				if ((obj = get_obj_here(ch, arg2)) == NULL)
				{
					send_to_char( "That does not seem to be here.\n\r", ch );
					pop_call();
					return;
				}
				vo = (void *) obj;
				break;
	
			case TAR_OBJ_CHAR_DEF:
				if (arg2[0] == '\0')
				{
					victim = ch;
				}
				else if ((victim = get_char_room(ch, arg2)) == NULL)
				{
					if ((obj = get_obj_carry(ch, arg2)) == NULL)
					{
						send_to_char( "Who or what should the spell be cast upon?\n\r", ch );
						pop_call();
						return;
					}
				}
				if (victim)
				{
					if (is_safe_magic(ch, victim, sn))
					{
						pop_call();
						return;
					}
					target = TAR_CHAR_DEFENSIVE;
					vo     = (void *) victim;
				}
				else
				{
					target = TAR_OBJ_INV;
					vo     = (void *) obj;
				}
				break;
	
			case TAR_OBJ_CHAR_OFF:
				if (arg2[0] == '\0')
				{
					if (who_fighting(ch) == NULL)
					{
						send_to_char("Cast the spell on whom?\n\r", ch);
						pop_call();
						return;
					}
					victim = who_fighting(ch);
				}
				else if ((victim = get_char_room(ch, arg2)) == NULL)
				{
					if ((obj = get_obj_carry(ch, arg2)) == NULL)
					{
						send_to_char( "Who or what should the spell be cast upon?\n\r", ch );
						pop_call();
						return;
					}
				}
				if (victim)
				{
					if (is_safe_magic(ch, victim, sn))
					{
						pop_call();
						return;
					}
					target = TAR_CHAR_OFFENSIVE;
					vo     = (void *) victim;
				}
				else
				{
					target = TAR_OBJ_INV;
					vo     = (void *) obj;
				}
				break;
		}
	}
	
	if (!Fail)
	{
		if (victim != NULL && victim != ch 
		&& IS_SET(skill_table[sn].flags, SF_TOUCH)
		&& !IS_SET(ch->cast_feats, METAMAGIC_REACH))
		{
			switch (target)
			{
				case TAR_CHAR_OFFENSIVE:
				case TAR_OBJ_CHAR_OFF:
					if(!check_hit(ch, victim, dice(1,20), 0, sn, NULL, TRUE, FALSE))
					{
						act( "You attempt to touch $N, but miss.", ch, NULL, victim, TO_CHAR);
						act( "$n attempts to touch $N, but misses.", ch, NULL, victim, TO_NOTVICT);
						act( "$n attempts to touch you, but misses.", ch, NULL, victim, TO_VICT);
						Fail = TRUE;
					}
					break;
				case TAR_CHAR_DEFENSIVE:
				case TAR_OBJ_CHAR_DEF:
					if((in_combat(victim) || in_combat(ch) || IS_AFFECTED(victim, AFF2_CONFUSION))
					&& !check_hit(ch, victim, dice(1,20), 0, sn, NULL, TRUE, FALSE))
					{
						act( "You attempt to touch $N, but miss.", ch, NULL, victim, TO_CHAR);
						act( "$n attempts to touch $N, but misses.", ch, NULL, victim, TO_NOTVICT);
						act( "$n attempts to touch you, but misses.", ch, NULL, victim, TO_VICT);
						Fail = TRUE;
					}
					break;
			}
		}
	}
	
	// Cannot turn or counterspell a racial ability.
	if (!Fail && !race)
	{
		/*
		 * Treature flag support for counterspell flag
		 */
		if (victim && (vic_obj = get_obj_tflag(victim, TFLAG_COUNTERSPELL)) != NULL)
		{
			if (vic_obj->value[1] == sn)
			{
				act( "$n's magic dissipates as $p counters it.", ch, vic_obj, victim, TO_VICT);
				act( "Your spell dissipates as $N counters it.",  ch, NULL, victim, TO_CHAR);
				act( "$n's spell dissipates as it reaches $N.",  ch, NULL, victim, TO_NOTVICT);
				vic_obj->value[1] = -1;
				Fail = TRUE;
			}
		}
		
		// Mud20 variant - Spell Turning = caster level check instead of level comp.
		if (!Fail && victim && victim != ch && is_affected(victim, gsn_spell_turning))
		{
			if (dice(1,20) + level < 11 + get_affect_level(victim, gsn_spell_turning))
			{
				act( "White lightning surrounds you, reflecting $n's magic.", ch, NULL, victim, TO_VICT    );
				act( "White lightning surrounds $N, reflecting your magic.",  ch, NULL, victim, TO_CHAR    );
				act( "White lightning surrounds $N, reflecting $n's magic.",  ch, NULL, victim, TO_NOTVICT );
				vo = (void *) ch;
				ch = victim;
				Turned = TRUE;
				affect_strip(victim, gsn_spell_turning);
			}
		}
	}

	if (!Fail)
	{
		ForReal = TRUE; // set to make spell_fun actually run
		
		// if the spell errors out, there's no cost and no prog trigger
		if (((*skill_table[sn].spell_fun) (sn, level, ch, vo, target)) == FALSE)
		{
			pop_call();
			return;
		}
		else
		{
			if (IS_SET(ch->cast_feats, METAMAGIC_REPEAT))
			{
				(*skill_table[sn].spell_fun) (sn, level, ch, vo, target);
			}
			if (!Turned) // for simplicity, no cast trigger on turned spells.
			{
				mprog_cast_trigger(ch, victim, sn);
				oprog_cast_trigger(ch, obj, sn);
				rprog_cast_trigger(ch, sn);
			}
		}
	}
	if (Turned) // make sure the right person pays the cost.
	{
		ch = (CHAR_DATA*) vo;
	}
	if (!race)
	{
		ch->mana[class] -= mana;
		check_components(ch, sn, TRUE);
		spell_favor(ch, sn);
	}
	else
	{
		ch->uses[sn]++;
	}
	pop_call();
	return;
}


/* 
 * Controls when characters release their magic
 * updated in main loop - Kregor
 */
void update_casting(void)
{
	CHAR_DATA *ch;
	CHAR_DATA *rch, *rch_next;

	push_call("update_casting()");
	
	for (ch = mud->f_char ; ch ; ch = mud->update_wch)
	{
		mud->update_wch = ch->next;

		if (ch->casting)
		{
			if (ch->cast_sn != gsn_recite_scroll
			&& (ch->cast_sn < 0 || !is_spell(ch->cast_sn)))
			{
				free_cast(ch);
			}

			if (in_combat(ch) && !is_active(ch))
				continue;

			ch->cast_timer++;
			
			if (ch->cast_timer == 2)
				say_spell(ch, ch->cast_sn);
				
			/* NPC opponents might take opportunity if they perceive they can. */
			if (ch->cast_timer == 5 && !learned(ch, gsn_warmage))
			{
				for (rch = ch->in_room->first_person ; rch ; rch = rch_next)
				{
					rch_next = rch->next_in_room;

					if (ch == rch || is_same_group(ch, rch))
						continue;

					if (IS_NPC(rch))
					{
						if (wis_roll(rch) < 10) // avg NPC has a 50% chance of taking the opportunity
							continue;
	
						if (who_fighting(rch) && who_fighting(rch) == ch)
						{
							if (attack_of_opportunity(rch, ch))
								continue;
						}
						if (counterspell(rch, ch, ch->cast_sn, ch->cast_level, FALSE))
						{
							if (ch->cast_sn == gsn_recite_scroll && ch->reciting)
								junk_obj(ch->reciting);
							free_cast(ch);
							break;
						}
					}
				}
			}
			
			if (!ch->casting)
				continue;
			
			if (ch->cast_timer == ch->casting_time)
			{
				if (ch->cast_sn == gsn_recite_scroll)
					finish_reciting(ch);
				else
					release_cast(ch);
				free_cast(ch);
				SET_BIT(ch->attack, ATTACK_CAST);
				turn_complete(ch);
				continue;
			}
			else if (!ch->cast_timer || ch->cast_timer % 8 == 0)
			{
				if (in_combat(ch) && is_active(ch))
				{
					turn_end(ch->in_battle->turn_list, 200 - ch->initiative);
					continue;
				}
			}
		}
	}
	pop_call();
	return;
}

/*
 * Free up the spell casting cue
 */
void free_cast( CHAR_DATA *ch )
{
	push_call("free_cast(%p)",ch);

	ch->casting 			= FALSE;
	
	ch->casting_time	= 0;
	ch->cast_timer		= 0;
	ch->cast_sn				= -1;
	ch->cast_level		= -1;
	ch->cast_circle		= -1;
	ch->cast_class		= 0;
	ch->cast_dir			= -1;
	ch->cast_feats		= 0;
	STRFREE(ch->target_name);
	ch->reciting			= NULL;
	pop_call();
	return;
}

/*
 * Return TRUE if spell is on char's spell list,
 * even if not high enough level to cast it, per d20 - Kregor
 */
bool can_brandish( CHAR_DATA *ch, int sn )
{
	int cnt;
	
	push_call("can_brandish(%p,%p)",ch,sn);
	
	for (cnt = 0 ; cnt < MAX_CLASS ; cnt++)
	{
		if (class_level(ch, cnt) && skill_table[sn].skill_level[cnt] < LEVEL_IMMORTAL)
		{
			pop_call();
			return TRUE;
		}
	}
	pop_call();
	return FALSE;
}


/*
 * Rolling up all the little items into one global USE command
 * includes support for multi-spell items, and ranged casting
 * and even invoking obj progs with the use command - Kregor
 */
void do_use( CHAR_DATA *ch, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;

	push_call("do_use(%p,%p)",ch,argument);
	
	argument = one_argument(argument, arg);
	
	if (arg[0] == '\0')
	{
		send_to_char("Use what?\n\r", ch);
		pop_call();
		return;
	}
	
	if ((obj = get_obj_wear(ch, arg)) == NULL)
	{
		send_to_char("You don't seem to be holding anything like that.\n\r", ch);
		pop_call();
		return;
	}
	/*
	 * note that putting a use trigger on an obj
	 * effectively makes other properties unusable - Kregor
	 */
	if (oprog_use_trigger(ch, obj))
	{
		pop_call();
		return;
	}
	else if (IS_OBJ_TYPE(obj, ITEM_STAFF)
	|| TREASURE_FLAG(obj, TFLAG_SPELL_STORING))
	{
		use_spell_item(ch, obj, argument);
	}
	else if (IS_OBJ_TYPE(obj, ITEM_WAND)
	|| TREASURE_FLAG(obj, TFLAG_SPELL_RECAST)
	|| TREASURE_FLAG(obj, TFLAG_SPELL_CHARGES))
	{
		use_item(ch, obj, argument);
	}
	else if (IS_OBJ_TYPE(obj, ITEM_SCROLL))
	{
		recite_scroll(ch, obj, argument);
	}
	else if (IS_OBJ_TYPE(obj, ITEM_POTION))
	{
		drink_obj(ch, obj);
	}
	else
	{
		act( "You attempt to call on the power of $p. Nothing happens.", ch, obj, NULL, TO_CHAR);
		act( "$n attempts to call on the power of $p. Nothing happens.", ch, obj, NULL, TO_ROOM);
	}
	pop_call();
	return;
}
	
	
void use_spell_item( CHAR_DATA *ch, OBJ_DATA *item, char *argument )
{
	char arg1[MAX_INPUT_LENGTH];
	char arg2[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	OBJ_DATA *obj;
	int sn, cnt, dir, val, level, range, cost, circle;
	bool ranged = FALSE;

	push_call("use_spell_item(%p,%p)",ch,argument);

	if (IS_SET(ch->action, ACTION_STANDARD))
	{	
		ch_printf_color(ch, "You have already made a standard action this round.\n\r");
		pop_call();
		return;
	}

	if (!item->identified)
	{
		send_to_char( "You have no idea what this can do!\n\r", ch );
		pop_call();
		return;
	}

	argument = one_argument( argument, arg1 );

	target_name = argument;
	argument = one_argument( argument, arg2 );
	
	if (*arg1 == '\0')
	{
		act( "What spell from $p do you wish to use?", ch, item, NULL, TO_CHAR);
		pop_call();
		return;
	}

	if ((sn = skill_lookup(arg1)) == -1)
	{
		act( "$t is not a spell.", ch, arg1, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	if ((circle = get_spell_circle(NULL, sn)) < 0)
	{
		send_to_char( "There's a bug with this code, notify a GM!\n\r", ch );
		bug("use_spell_item (%d): bad spell circle.", item->pIndexData->vnum);
		pop_call();
		return;
	}

	/*
	 * Determine whether it's a ranged cast or not - Kregor
	 */
	if (*arg2 != '\0'
	&& skill_table[sn].target != TAR_IGNORE
	&& skill_table[sn].target != TAR_CHAR_SELF
	&& skill_table[sn].target != TAR_OBJ_INV
	&& skill_table[sn].target != TAR_OBJ_WIELD)
	{
		if ((dir = direction_door(arg2)) != -1)
		{
			ranged = TRUE;
			target_name = argument;
			argument    = one_argument( argument, arg2 );
		}
	}

	val = 1;
	
	switch (item->item_type)
	{
		case ITEM_STAFF:
			for (cnt = 3 ; cnt <= 6 ; cnt++)
			{
				if (item->value[cnt] < 0)
					continue;					
				if (!is_spell(item->value[cnt]) || skill_table[item->value[cnt]].spell_fun == 0)
				{
					bug( "use_spell_item in object %d: bad sn %d in obj value %d.", item->pIndexData->vnum, item->value[cnt], cnt );
					pop_call();
					return;
				}
				if (item->value[cnt] == sn)
				{
					val = cnt;
					break;
				}
			}
			break;
		case ITEM_TREASURE:
			if (TREASURE_FLAG(item, TFLAG_SPELL_STORING))
			{
				for (cnt = 1 ; cnt <= 3 ; cnt++)
				{
					if (item->value[cnt] < 0)
						continue;
					if (!is_spell(item->value[cnt]) || skill_table[item->value[cnt]].spell_fun == 0)
					{
						bug( "use_spell_item in object %d: bad sn %d in obj value %d.", item->pIndexData->vnum, item->value[cnt], cnt );
						pop_call();
						return;
					}
					if (item->value[cnt] == sn)
					{
						val = cnt;
						break;
					}
				}
			}
			break;
	}
	if (val == -1)
	{
		act( "You attempt to call on the power of $p. Nothing happens.", ch, item, NULL, TO_CHAR);
		act( "$n attempts to call on the power of $p. Nothing happens.", ch, item, NULL, TO_ROOM);
		pop_call();
		return;
	}

	level = item_caster_level(item, sn);
	
	if (IS_OBJ_TYPE(item, ITEM_STAFF))
	{
		if ((cost = UMAX(1, circle * 2 / 3)) > item->value[2])
		{
			act( "There aren't enough charges left on $p.", ch, item, NULL, TO_CHAR);
			act( "$n attempts to brandish $p... and nothing happens.", ch, item, NULL, TO_ROOM);
			pop_call();
			return;
		}
	}
	
	level = UMAX(multi_caster_level(ch, sn), level);

	if (ranged && (range = get_spell_range(level, sn)) <= 0)
	{	
		ch_printf_color(ch, "That spell cannot be cast at range.\n\r");
		pop_call();
		return;
	}
	
	victim	= NULL;
	obj			= NULL;

	if (arg2[0] == '\0')
	{
		victim = ch;
	}
	else if (ranged)
	{
		if ((victim = find_char_dir(ch, dir, arg2)) == NULL)
		{
			send_to_char("You cannot find your target.\n\r", ch);
			pop_call();
			return;
		}
		else if (findpath_search_victim(ch, victim, range) == -1)
		{
			send_to_char("That target is out of range.\n\r", ch);
			pop_call();
			return;
		}
	}
	else
	{
		if ((victim = get_char_room (ch, arg2)) == NULL
		&&  (obj = get_obj_here(ch, arg2)) == NULL)
		{
			send_to_char("You cannot find your target.\n\r", ch);
			pop_call();
			return;
		}
	}
	
	CHECK_TURN(ch, victim);

	if (IS_OBJ_TYPE(item, ITEM_STAFF) && !can_brandish(ch, sn) && !use_magic_check(ch, NULL, use_magic_roll(ch) + synergy_bonus(ch, gsn_spellcraft), 20 + item->value[0]))
	{
		act( "You fail to invoke $p.",  ch, item, NULL, TO_CHAR);
		act( "$n attempts to brandish $p... and nothing happens.", ch, item, NULL, TO_ROOM);
		TAKE_ACTION(ch, ACTION_STANDARD);
		pop_call();
		return;
	}
	
	act( "You brandish $p.",  ch, item, NULL, TO_CHAR );
	act( "$n brandishes $p.", ch, item, NULL, TO_ROOM );

	if (obj_cast_spell(item->value[val], level, ch, victim, obj))
	{
		if (IS_OBJ_TYPE(item, ITEM_STAFF) && (item->value[2] -= cost) <= 0)
		{
			act( "$p blazes bright and loses its magical luster.", ch, item, NULL, TO_CHAR );
			act( "$p blazes bright and loses its magical luster.", ch, item, NULL, TO_ROOM );
			REMOVE_BIT(item->extra_flags, ITEM_MAGIC);
		}
		if (TREASURE_FLAG(item, TFLAG_SPELL_STORING))
		{
			item->value[val] = -1;
			act( "The spell is discharged from $p", ch, item, NULL, TO_CHAR );
		}
		TAKE_ACTION(ch, ACTION_STANDARD);
	}
	pop_call();
	return;
}


void use_item( CHAR_DATA *ch, OBJ_DATA *item, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	OBJ_DATA *obj;
	int sn, level, dir, range;
	bool ranged = FALSE;

	push_call("use_item(%p,%p,%p)",ch,item,argument);

	target_name = argument;
	argument = one_argument( argument, arg );

	if ((sn = item->value[3]) == -1 || !is_spell(sn))
	{
		act( "$p strangely fizzles.", ch, item, NULL, TO_CHAR);
		bug( "use_item in object %d: bad sn %d.", item->pIndexData->vnum, item->value[3]);
		pop_call();
		return;
	}

	/*
	 * Determine whether it's a ranged cast or not
	 */
	if (*arg != '\0'
	&& skill_table[sn].target != TAR_IGNORE
	&& skill_table[sn].target != TAR_CHAR_SELF
	&& skill_table[sn].target != TAR_OBJ_INV
	&& skill_table[sn].target != TAR_OBJ_WIELD)
	{
		if ((dir = direction_door(arg)) != -1)
		{
			ranged = TRUE;
			target_name = argument;
			argument    = one_argument( argument, arg );
		}
	}

	level = item_caster_level(item, sn);
	obj = NULL;
	victim = NULL;

	if (ranged && (range = get_spell_range(level, sn)) <= 0)
	{	
		ch_printf_color(ch, "That spell cannot be cast at range.\n\r");
		pop_call();
		return;
	}
	
	if (arg[0] == '\0')
	{
		victim = ch;
	}
	else if (ranged)
	{
		if ((victim = find_char_dir(ch, dir, arg)) == NULL)
		{
			send_to_char("You cannot find your target.\n\r", ch);
			pop_call();
			return;
		}
		else if (findpath_search_victim(ch, victim, range) == -1)
		{
			send_to_char("That target is out of range of that spell.\n\r", ch);
			pop_call();
			return;
		}
	}
	else
	{
		if ((victim = get_char_room (ch, arg)) == NULL
		&&  (obj = get_obj_here(ch, arg)) == NULL)
		{
			send_to_char( "You cannot find it.\n\r", ch );
			pop_call();
			return;
		}
	}

	CHECK_TURN(ch, victim);

	if (item->value[2] == 0)
	{
		act( "$p has no charges left.", ch, item, NULL, TO_CHAR );
		pop_call();
		return;
	}

	if (IS_OBJ_TYPE(item, ITEM_WAND))
	{
		if (!can_brandish(ch, sn) && !use_magic_check(ch, NULL, use_magic_roll(ch) + synergy_bonus(ch, gsn_spellcraft), 20 + item->value[0]))
		{
			act( "You fail to invoke $p.",  ch, item, NULL, TO_CHAR);
			act( "$n attempts to brandish $p... and nothing happens.", ch, item, NULL, TO_ROOM);
	
			TAKE_ACTION(ch, ACTION_STANDARD);
			pop_call();
			return;
		}
	}
	
	act( "You brandish $p.",  ch, item, NULL, TO_CHAR );
	act( "$n brandishes $p.", ch, item, NULL, TO_ROOM );

	if (obj_cast_spell(item->value[3], item->value[0], ch, victim, obj))
	{
		if (item->value[2] > 0)
		{
			if (--item->value[2] == 0 && IS_OBJ_TYPE(item, ITEM_WAND))
			{
				act( "$p turns into a simple stick.", ch, item, NULL, TO_CHAR );
				act( "$p turns into a simple stick.", ch, item, NULL, TO_ROOM );
				REMOVE_BIT(item->extra_flags, ITEM_MAGIC);
			}
		}
		TAKE_ACTION(ch, ACTION_STANDARD);
	}
	pop_call();
	return;
}


/*
 * New recite for D20 scroll usage - Kregor 10/25/07
 * folded into the do_use command - 12/28/07
 */
void recite_scroll( CHAR_DATA *ch, OBJ_DATA *scroll, char *argument )
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	OBJ_DATA *obj;
	bool ranged = FALSE;
	bool checked;
	int sn, level, dir, range, class, eff_level;

	push_call("recite_scroll(%p,%p)",ch,scroll,argument);

	if (IS_SET(ch->action, ACTION_STANDARD))
	{	
		ch_printf_color(ch, "You have already made a standard action this round.\n\r");
		pop_call();
		return;
	}

	target_name = argument;
	argument = one_argument( argument, arg );
	dir = -1;

	/*
	 * Determine whether it's a ranged cast or not
	 */
	if (arg[0] != '\0')
	{
		ch_printf_color(ch, "finding direction: %s\n\r", arg);
		if ((dir = direction_door(arg)) != -1)
		{
			ranged = TRUE;
			target_name = argument;
			argument    = one_argument( argument, arg );
		}
	}

	if (scroll->item_type != ITEM_SCROLL)
	{
		send_to_char( "You can recite only scrolls.\n\r", ch );
		pop_call();
		return;
	}

	if ((sn = scroll->value[1]) < 0 || !is_spell(sn))
	{
		if (sn > 0 && skill_table[sn].skilltype == FSKILL_BARDSONG)
		{
			send_to_char( "You cannot empower sheet music by reciting it.\n\r", ch );
			pop_call();
			return;
		}
		if (!IS_NPC(ch) && ch->desc == NULL)
		{
			log_printf("[%u] reciting unknown spell: %s", ch->pIndexData->vnum, arg);
		}
		send_to_char( "That is not a spell.\n\r", ch );
		pop_call();
		return;
	}
	
	if ((level = scroll->value[0]) <= 0)
	{
		log_printf("Bug: Reciting scroll with no level: %s", scroll->name);
		send_to_char( "Something went wrong, notify an immortal.\n\r", ch );
		pop_call();
		return;
	}
	
	checked = use_magic_check(ch, NULL, use_magic_roll(ch) + synergy_bonus(ch, gsn_decipher_script) + is_affected(ch, gsn_read_magic), 20 + scroll->value[0]);

	if ((class = multi(ch, sn)) == -1 && !checked)
	{
		send_to_char( "There is no spell upon the scroll you can recite.\n\r", ch);
		pop_call();
		return;
	}

	if (is_safe_magic(ch, NULL, sn))
	{
		pop_call();
		return;
	}

	if (ranged && (range = get_spell_range(level, sn)) <= 0)
	{	
		ch_printf_color(ch, "That spell cannot be cast at that range.\n\r");
		pop_call();
		return;
	}
	
	victim	= NULL;
	obj			= NULL;

	if (arg[0] == '\0')
	{
		victim = ch;
	}
	else if (ranged)
	{
		if ((victim = find_char_dir(ch, dir, arg)) == NULL)
		{
			send_to_char("You cannot find your target.\n\r", ch);
			pop_call();
			return;
		}
		else if (findpath_search_victim(ch, victim, range) == -1)
		{
			send_to_char("That target is out of range of your spell.\n\r", ch);
			pop_call();
			return;
		}
	}
	else
	{
		if ((victim = get_char_room (ch, arg)) == NULL
		&&  (obj = get_obj_here(ch, arg)) == NULL)
		{
			send_to_char("You cannot find your target.\n\r", ch);
			pop_call();
			return;
		}
	}

	CHECK_TURN(ch, victim);

	eff_level = multi_caster_level(ch, sn);
	if (multi(ch, sn) == -1 && checked)
		eff_level = multi_class_level(ch, gsn_use_magic);
	
	ch->casting 			= TRUE;
	ch->casting_time	= 8;
	ch->cast_sn				= gsn_recite_scroll;
	ch->cast_level		= eff_level;
	ch->cast_circle		= get_spell_circle(NULL, sn);
	ch->cast_dir			= dir;
	ch->cast_class		= class;
	ch->cast_feats		= scroll->value[2];
	ch->reciting			= scroll;
	ch->target_name 	= STRALLOC(target_name);

	act( "You begin to recite $p.", ch, scroll, NULL, TO_CHAR );
	act( "$n begins to recite $p.", ch, scroll, NULL, TO_ROOM );
	
	pop_call();
	return;
}


/*
 * Release the spell from a scroll
 */
void finish_reciting( CHAR_DATA *ch )
{
	char *argument;
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim, *vch, *vch_next;
	OBJ_DATA *obj, *scroll;
	bool ranged = FALSE;
	int sn, diceroll, dc, level, dir, range;

	push_call("finish_reciting(%p)",ch);

	target_name = STRALLOC(ch->target_name);
	argument = STRALLOC(ch->target_name);

	if ((scroll = ch->reciting) == NULL || scroll->item_type != ITEM_SCROLL)
	{
		send_to_char("Um, where did your scroll go?\n\r", ch);
		bug("finish_reciting: NULL scroll",0);
		pop_call();
		return;
	}
	
	if ((level = ch->cast_level) <= 0)
	{
		act( "Try as you might, you cannot seem to recite $p.", ch, scroll, NULL, TO_CHAR);
		pop_call();
		return;
	}
		
	if ((sn = scroll->value[1]) < 0 || !is_spell(sn))
	{
		if (!IS_NPC(ch) && ch->desc == NULL)
		{
			log_printf("[%u] reciting unknown spell: %s", ch->pIndexData->vnum, arg);
		}
		send_to_char( "That is not a spell.\n\r", ch );
		pop_call();
		return;
	}

	if ((dir = ch->cast_dir) != -1)
	{
		ranged = TRUE;
	}
	if (ranged && (range = get_spell_range(level, sn)) <= 0)
	{	
		ch_printf_color(ch, "That spell cannot be cast at that range.\n\r");
		pop_call();
		return;
	}

	
	argument = one_argument( argument, arg );

	/*
	 * find the target again, in case something gets away. :)
	 */
	victim	= NULL;
	obj			= NULL;

	if (arg[0] == '\0')
	{
		victim = ch;
	}
	else if (ranged)
	{
		if ((victim = find_char_dir(ch, dir, arg)) == NULL)
		{
			send_to_char("You cannot find your target.\n\r", ch);
			pop_call();
			return;
		}
		else if (findpath_search_victim(ch, victim, range) == -1)
		{
			send_to_char("That target is out of range of your spell.\n\r", ch);
			pop_call();
			return;
		}
	}
	else
	{
		if ((victim = get_char_room (ch, arg)) == NULL
		&&  (obj = get_obj_here(ch, arg)) == NULL)
		{
			send_to_char("You cannot find your target.\n\r", ch);
			pop_call();
			return;
		}
	}

	dc = scroll->value[0] + 1;
	diceroll = dice(1,20) + level;
	
	if (level < scroll->value[0])
	{
		if (diceroll < dc)
		{
			if ((diceroll = wis_roll(ch)) == 1 || diceroll < 5)
			{
				switch (number_bits(2))
				{
					case 0:
						send_to_char("Something in your throat prevents you from reciting properly.\n\r", ch);
						act( "$n recites $p, and nothing happens.", ch, scroll, NULL, TO_ROOM );
						break;
					case 1:
						act( "You bumble your words causing the scroll to erupt in magical energy!\n\r", ch, NULL, NULL, TO_CHAR);
						act( "$n bumbles reciting $p, and the scroll erupts in magical energy.", ch, scroll, NULL, TO_ROOM );
						ch->hit -= dice(scroll->value[0], 6);
						break;
					case 2:
						if (victim != ch)
							victim = ch;
						else
						{
							for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
							{
								vch_next = vch->prev_in_room;
								
								if (!can_mass_cast(ch, vch, sn) || is_same_group(ch, vch))
									continue;

								victim = vch;
								break;
							}
						}
						act( "You mispronounce a syllable, and something goes really wrong.\n\r", ch, NULL, NULL, TO_CHAR);
						act( "$n recites $p, and things don't go quite right.", ch, scroll, NULL, TO_ROOM );
						obj_cast_spell(scroll->value[1], scroll->value[0], ch, victim, obj);
						break;
					case 3:
						send_to_char("You hesitate mid-way through the reciting and nothing happens.\n\r", ch);
						act( "$n recites $p, and nothing happens.", ch, scroll, NULL, TO_ROOM );
						break;
				}
				junk_obj(scroll);
				TAKE_ACTION(ch, ACTION_STANDARD);
				pop_call();
				return;
			}
			send_to_char("You failed to recite the scroll properly.\n\r", ch);
			act( "$n recites $p, and nothing happens.", ch, scroll, NULL, TO_ROOM );
			TAKE_ACTION(ch, ACTION_STANDARD);
			junk_obj(scroll);
			pop_call();
			return;
		}
	}

	diceroll = concentration_roll(ch);

	if (IS_ENTANGLED(ch) || drunk_level(ch) >= DRUNK_TIPSY || is_affected(ch, gsn_insect_plague) || ch->grappling || ch->grappled_by)
	{
		int DC;
		
		if (ch->grappled_by)
			DC = 10 + combat_maneuver_bonus(ch->grappled_by) + ch->cast_circle;
		else
			DC = 15 + ch->cast_circle;
			
		if (!concentration_check(ch, victim, diceroll, DC))
		{
			send_to_char("{138}You lost your concentration, ruining the spell!\n\r", ch);
			act( "Magical energy fizzles as $p vanishes from $n's hands!", ch, scroll, NULL, TO_ROOM);
			junk_obj(scroll);
			TAKE_ACTION(ch, ACTION_STANDARD);
			pop_call();
			return;
		}
	}	

	act( "You finish reciting $p.", ch, scroll, NULL, TO_CHAR );
	act( "$n finishes reciting $p.", ch, scroll, NULL, TO_ROOM );
	
	if (obj_cast_spell(sn, scroll->value[0], ch, victim, obj))
	{
		TAKE_ACTION(ch, ACTION_STANDARD);
		junk_obj(scroll);
	}
	pop_call();
	return;
}

/*
 * Cast spells at targets using a magical object.
 * revamped for D20 goodness, made bool instead of void
 */
bool obj_cast_spell( int sn, int level, CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *obj )
{
	OBJ_DATA *vic_obj;
	void *vo;
	int target;

	push_call("obj_cast_spell(%p,%p,%p,%p,%p)",sn,level,ch,victim,obj);

	if (sn <= 0)
	{
		pop_call();
		return FALSE;
	}

	if (is_safe_magic(ch, NULL, sn))
	{
		pop_call();
		return FALSE;
	}

	if (!is_spell(sn) || skill_table[sn].spell_fun == 0)
	{
		if (obj != NULL)
		{
			log_printf("obj_cast_spell: Vnum %u bad sn %d.", obj->pIndexData->vnum, sn);
		}
		else
		{
			log_printf("Unknown item cast sn %d", sn);
		}
		pop_call();
		return FALSE;
	}

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

		case TAR_IGNORE:
			vo = NULL;
			break;

		case TAR_CHAR_OFFENSIVE:
			if (victim == ch || victim == NULL)
			{
				victim = who_fighting(ch);
			}
			if (victim == NULL)
			{
				send_to_char( "Your target must have slipped away.\n\r", ch );
				pop_call();
				return FALSE;
			}
			if (is_safe_magic(ch, victim, sn))
			{
				pop_call();
				return FALSE;
			}
			vo = (void *) victim;
			break;

		case TAR_UNDEAD_DEF:
		case TAR_UNDEAD_OFF:
			if (victim == ch || victim == NULL)
			{
				send_to_char( "Your must specify your target.\n\r", ch );
				pop_call();
				return FALSE;
			}
			if (target == TAR_UNDEAD_OFF && IS_UNDEAD(victim) && is_safe_magic(ch, victim, sn))
			{
				pop_call();
				return FALSE;
			}
			vo = (void *) victim;
			if (target == TAR_UNDEAD_OFF)
				target = TAR_CHAR_OFFENSIVE;
			else
				target = TAR_CHAR_DEFENSIVE;
			break;

		case TAR_CHAR_DEFENSIVE:
			if (victim == NULL)
			{
				victim = ch;
			}
			if (is_safe_magic(ch, victim, sn))
			{
				pop_call();
				return FALSE;
			}
			vo = (void *) victim;
			break;

		case TAR_CHAR_SELF:
			if (victim == NULL)
			{
				victim = ch;
			}
			vo = (void *) ch;
			break;

		case TAR_OBJ_INV:
		case TAR_OBJ_WIELD:
		case TAR_OBJ_ROOM:
			if (obj == NULL)
			{
				send_to_char( "Where is your target?\n\r", ch );
				pop_call();
				return FALSE;
			}
			vo = (void *) obj;
			break;

		case TAR_OBJ_CHAR_DEF:
			if (victim)
			{
				if (is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return FALSE;
				}
				target = TAR_CHAR_DEFENSIVE;
				vo     = (void *) victim;
			}
			else
			{
				target = TAR_OBJ_INV;
				vo     = (void *) obj;
			}
			break;

		case TAR_OBJ_CHAR_OFF:
			if (victim)
			{
				if (victim == ch && who_fighting(ch))
				{
					victim = who_fighting(ch);
				}
				if (is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return FALSE;
				}
				target = TAR_CHAR_OFFENSIVE;
				vo     = (void *) victim;
			}
			else
			{
				target = TAR_OBJ_INV;
				vo     = (void *) obj;
			}
			break;
	}

	if (victim != NULL && victim != ch 
	&& IS_SET(skill_table[sn].flags, SF_TOUCH)
	&& !IS_SET(ch->cast_feats, METAMAGIC_REACH))
	{
		// touch spells from or to incorporeals do not work - Kregor
		if ((IS_INCORPOREAL(ch) && !IS_INCORPOREAL(victim))
		|| (IS_INCORPOREAL(victim) && !IS_INCORPOREAL(ch)))
		{
			act("Your touch passes right through $N.", ch, NULL, victim, TO_CHAR);
			act("$n's touch passes right through $N.", ch, NULL, victim, TO_NOTVICT);
			act("$n's touch passes right through you.", ch, NULL, victim, TO_VICT);
			pop_call();
			return FALSE;
		}
		// if you don't hit with a touch attack, the spell is wasted
		switch (target)
		{
			case TAR_CHAR_OFFENSIVE:
			case TAR_OBJ_CHAR_OFF:
				if (!can_melee_attack(ch, victim))
				{
					pop_call();
					return TRUE;
				}
				if(!check_hit(ch, victim, dice(1,20), 0, sn, NULL, TRUE, FALSE))
				{
					act( "You attempt to touch $N, but miss.", ch, NULL, victim, TO_CHAR);
					act( "$n attempts to touch $N, but misses.", ch, NULL, victim, TO_NOTVICT);
					act( "$n attempts to touch you, but misses.", ch, NULL, victim, TO_VICT);
					pop_call();
					return TRUE;
				}
				break;
			case TAR_CHAR_DEFENSIVE:
			case TAR_OBJ_CHAR_DEF:
				// must succeed a touch on ally if casting in melee - Kregor
				if((in_combat(victim) || in_combat(ch)) && !check_hit(ch, victim, dice(1,20), 0, sn, NULL, TRUE, FALSE))
				{
					act( "You attempt to touch $N, but miss.", ch, NULL, victim, TO_CHAR);
					act( "$n attempts to touch $N, but misses.", ch, NULL, victim, TO_NOTVICT);
					act( "$n attempts to touch you, but misses.", ch, NULL, victim, TO_VICT);
					pop_call();
					return TRUE;
				}
				break;
		}
	}
	
	if ((vic_obj = get_obj_tflag(ch, TFLAG_COUNTERSPELL)) != NULL)
	{
		if (vic_obj->value[1] == sn)
		{
			act( "$n's magic dissipates as $p counters it.", ch, vic_obj, victim, TO_VICT);
			act( "Your spell dissipates as $N counters it.",  ch, NULL, victim, TO_CHAR);
			act( "$n's spell dissipates as it reaches $N.",  ch, NULL, victim, TO_NOTVICT);
			vic_obj->value[1] = -1;
			pop_call();
			return TRUE;
		}
	}
	
	/*
	 * Mud20 variant - Spell Turning = caster level check
	 */
	if (victim && victim != ch && is_affected(victim, gsn_spell_turning))
	{
		if (dice(1,20) + level < 11 + get_affect_level(victim, gsn_spell_turning))
		{
			act( "White lightning surrounds you, reflecting $n's magic.", ch, NULL, victim, TO_VICT);
			act( "White lightning surrounds $N, reflecting your magic.",  ch, NULL, victim, TO_CHAR);
			act( "White lightning surrounds $N, reflecting $n's magic.",  ch, NULL, victim, TO_NOTVICT);
			vo = (void *) ch;
// 			ch = victim;
			affect_strip(victim, gsn_spell_turning);
		}
	}
	

	if ((*skill_table[sn].spell_fun) ( sn, level, ch, vo, target ))
	{
		if (target == TAR_CHAR_OFFENSIVE)
		{
			if (can_see_casting(victim, ch, sn))
				make_char_fight_char( ch, victim );
		}
	}
	pop_call();
	return TRUE;
}


/*
 * For spell storing weapon flag
 */
bool weapon_cast_spell( int sn, int level, CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *obj )
{
	OBJ_DATA *vic_obj;
	void *vo;
	int target;

	push_call("obj_cast_spell(%p,%p,%p,%p,%p)",sn,level,ch,victim,obj);

	if (obj == NULL)
	{
		pop_call();
		return FALSE;
	}

	if (!WEAPON_FLAG(obj, WFLAG_SPELL_STORING))
	{
		pop_call();
		return FALSE;
	}
	if (sn <= 0)
	{
		pop_call();
		return FALSE;
	}

	if (is_safe_magic(ch, NULL, sn))
	{
		pop_call();
		return FALSE;
	}

	if (!is_spell(sn) || skill_table[sn].spell_fun == 0)
	{
		if (obj != NULL)
		{
			log_printf("obj_cast_spell: Vnum %u bad sn %d.", obj->pIndexData->vnum, sn);
		}
		else
		{
			log_printf("Unknown item cast sn %d", sn);
		}
		pop_call();
		return FALSE;
	}

	target = skill_table[sn].target;

	switch (target)
	{
		default:
			bug( "obj_cast_spell: bad target for sn %d.", sn );
			pop_call();
			return TRUE;

		case TAR_IGNORE:
			vo = NULL;
			break;

		case TAR_CHAR_OFFENSIVE:
			if (victim == ch || victim == NULL)
			{
				victim = who_fighting(ch);
			}
			if (victim == NULL)
			{
				send_to_char( "Your target must have slipped away.\n\r", ch );
				pop_call();
				return TRUE;
			}
			if (is_safe_magic(ch, victim, sn))
			{
				pop_call();
				return FALSE;
			}
			vo = (void *) victim;
			break;

		case TAR_UNDEAD_DEF:
		case TAR_UNDEAD_OFF:
			if (victim == ch || victim == NULL)
			{
				send_to_char( "Your must specify your target.\n\r", ch );
				pop_call();
				return TRUE;
			}
			if (target == TAR_UNDEAD_OFF && IS_UNDEAD(victim) && is_safe_magic(ch, victim, sn))
			{
				pop_call();
				return FALSE;
			}
			vo = (void *) victim;
			if (target == TAR_UNDEAD_OFF)
				target = TAR_CHAR_OFFENSIVE;
			else
				target = TAR_CHAR_DEFENSIVE;
			break;

		case TAR_CHAR_DEFENSIVE:
			if (victim == NULL)
			{
				victim = ch;
			}
			if (is_safe_magic(ch, victim, sn))
			{
				pop_call();
				return FALSE;
			}
			vo = (void *) victim;
			break;

		case TAR_CHAR_SELF:
			if (victim == NULL)
			{
				victim = ch;
			}
			vo = (void *) ch;
			break;

		case TAR_OBJ_CHAR_DEF:
			if (victim)
			{
				if (is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return FALSE;
				}
				target = TAR_CHAR_DEFENSIVE;
				vo     = (void *) victim;
			}
			else
			{
				bug( "obj_cast_spell: bad target for sn %d.", sn );
				pop_call();
				return TRUE;
			}
			break;

		case TAR_OBJ_CHAR_OFF:
			if (victim)
			{
				if (victim == ch && who_fighting(ch))
				{
					victim = who_fighting(ch);
				}
				if (is_safe_magic(ch, victim, sn))
				{
					pop_call();
					return FALSE;
				}
				target = TAR_CHAR_OFFENSIVE;
				vo     = (void *) victim;
			}
			else
			{
				bug( "obj_cast_spell: bad target for sn %d.", sn );
				pop_call();
				return TRUE;
			}
			break;
	}

	if ((vic_obj = get_obj_tflag(ch, TFLAG_COUNTERSPELL)) != NULL)
	{
		if (vic_obj->value[1] == sn)
		{
			act( "$n's magic dissipates as $p counters it.", ch, vic_obj, victim, TO_VICT);
			act( "Your spell dissipates as $N counters it.",  ch, NULL, victim, TO_CHAR);
			act( "$n's spell dissipates as it reaches $N.",  ch, NULL, victim, TO_NOTVICT);
			vic_obj->value[1] = -1;
			pop_call();
			return TRUE;
		}
	}
	
	/*
	 * Mud20 variant - Spell Turning = caster level check
	 */
	if (victim && victim != ch && is_affected(victim, gsn_spell_turning))
	{
		if (dice(1,20) + level < 11 + get_affect_level(victim, gsn_spell_turning))
		{
			act( "White lightning surrounds you, reflecting $n's magic.", ch, NULL, victim, TO_VICT);
			act( "White lightning surrounds $N, reflecting your magic.",  ch, NULL, victim, TO_CHAR);
			act( "White lightning surrounds $N, reflecting $n's magic.",  ch, NULL, victim, TO_NOTVICT);
			vo = (void *) ch;
// 			ch = victim;
			affect_strip(victim, gsn_spell_turning);
		}
	}
	

	(*skill_table[sn].spell_fun) ( sn, level, ch, vo, target );

	pop_call();
	return TRUE;
}


/*
	Magical Totem casting spells on the room
*/
void totem_cast_spell( OBJ_DATA *obj )
{
	CHAR_DATA *totem, *rch, *rch_next;
	int sn, level, target;

	push_call("totem_cast_spell(%p)",obj);

	if (obj->in_room == NULL)
	{
		pop_call();
		return;
	}

	if (obj->in_room->area->nplayer == 0 || obj->in_room->nplayer == 0)
	{
		pop_call();
		return;
	}

	if ((totem = mob_index[MOB_VNUM_TOTEM]->first_instance) == NULL)
	{
		pop_call();
		return;
	}

	target_name = "";

	STRFREE(totem->short_descr);
	totem->short_descr = STRDUPE(obj->short_descr);

	char_to_room(totem, obj->in_room->vnum, FALSE);

	act( "The air thickens with magic as $n starts to hum.", totem, NULL, NULL, TO_ROOM);

	for (rch = obj->in_room->first_person ; rch ; rch = rch_next)
	{
		rch_next = rch->next_in_room;

		if (rch == totem)
		{
			continue;
		}

		if (IS_NPC(rch) && IS_SET(rch->act, ACT_MOBINVIS))
		{
			continue;
		}

		sn = obj->value[number_range(0,3)];

		if (!is_spell(sn))
		{
			continue;
		}

		level  = obj->level;
		target = skill_table[sn].target;

		switch (target)
		{
			case TAR_OBJ_CHAR_OFF:
				target = TAR_CHAR_OFFENSIVE;
				break;
			case TAR_OBJ_CHAR_DEF:
				target = TAR_CHAR_DEFENSIVE;
				break;
		}

		(*skill_table[sn].spell_fun) (sn, level, totem, rch, target);
	}

	char_to_room(totem, ROOM_VNUM_TOTEM, FALSE);

	pop_call();
	return;
}


/*
 * dismiss a dismissable spell if you were the caster - Kregor 10/1/07
 */
void do_dismiss (CHAR_DATA *ch, char *argument)
{
	AFFECT_DATA *paf, *paf_next;
	CHAR_DATA *victim;
	int paf_type, sn;
	char arg1[MAX_INPUT_LENGTH];

	push_call("do_dismiss(%p,%p)",ch,argument);
	
	if (IS_NPC(ch))
	{
		send_to_char("You did not dismiss anything.\n\r", ch);
		pop_call();
		return;
	}
	argument = one_argument(argument, arg1);
	
	if ((sn = skill_lookup(arg1)) == -1)
	{
		send_to_char("That is not a spell.\n\r", ch);
		pop_call();
		return;
	}
	
	if (argument[0] == '\0')
	{
		victim = ch;
	}
	else if ((victim = get_char_room(ch, argument)) == NULL)
	{
		send_to_char("That person is not here.\n\r", ch);
		pop_call();
		return;
	}
	
	for ( paf_type = 0, paf = victim->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;

		if( paf->type != sn )
			continue;
		if (!is_spell(paf->type))
			continue;

		if (!is_name(paf->caster, ch->name))
		{
			send_to_char("You can only dismiss spells cast by you.\n\r", ch);
			continue;
		}
		
		if (paf->duration < 0)
		{
			send_to_char("You cannot dismiss permanent affects.\n\r", ch);
			continue;
		}

		if (!IS_SET(skill_table[sn].flags, SF_DISMISSABLE))
		{
			send_to_char("That spell cannot be dismissed.\n\r", ch);
			break;
		}
		
		if (paf->duration >= 0 && paf_type == 0)
		{
			if (skill_table[paf->type].msg_off)
			{
				act( skill_table[paf->type].msg_off, victim, NULL, NULL, TO_CHAR);
			}
			if (skill_table[paf->type].msg_off_room)
			{
				act( skill_table[paf->type].msg_off_room, victim, NULL, NULL, TO_ROOM);
			}
		}
		paf_type = paf->type;
		affect_from_char(victim, paf);
	}

	if (paf_type == 0)
	{
		send_to_char( "You did not dismiss anything.\n\r", ch );
		pop_call();
		return;
	}
	else if (victim != ch && !is_string(skill_table[paf->type].msg_off_room))
	{
		act( "You dismiss $t from $N.", ch, skill_table[sn].name, victim, TO_CHAR );
	}
	TAKE_ACTION(ch, ACTION_STANDARD);
	pop_call();
	return;
}


/*
	Returns TRUE and gives appropriate spams if one cannot cast a spell
	on the victim, if victim is NULL only room and ch based stuff is
	checked - Scandum 01-04-2002
*/
bool is_safe_magic( CHAR_DATA *ch, CHAR_DATA *victim, int sn)
{
	push_call("is_safe_magic(%p,%p)",ch,victim);

	if (IS_SET(ch->in_room->room_flags, ROOM_SAFE))
	{
		if (skill_table[sn].target == TAR_IGNORE)
		{
			if (skill_table[sn].minimum_position == POS_FIGHTING)
			{
				send_to_char( "Wards prevent you from doing that here.\n\r", ch);
				pop_call();
				return TRUE;
			}
		}
		if (skill_table[sn].target == TAR_CHAR_OFFENSIVE
		||  skill_table[sn].target == TAR_OBJ_CHAR_OFF)
		{
			send_to_char( "Wards prevent you from doing that here.\n\r", ch);
			pop_call();
			return TRUE;
		}
	}

	//antimagic suppresses both spells and supernatural abilities - Kregor
	if (is_spell(sn) || IS_SET(skill_table[sn].flags, SF_SUPERNATURAL|SF_SPELL_LIKE))
	{
		if (IS_SET(ch->in_room->room_flags, ROOM_NO_MAGIC))
		{
			send_to_char( "Your magic fizzles.\n\r",ch);
			pop_call();
			return TRUE;
		}
		// and is beyond the interest of transformed combatants.
		if (is_affected(ch, gsn_transformation))
		{
			send_to_char( "You're too thrilled with battle right now!\n\r",ch);
			pop_call();
			return TRUE;
		}
	}

	if (IS_SET(skill_table[sn].flags, SF_NONCOMBAT))
	{
		if (in_combat(ch))
		{
			send_to_char( "This effect cannot be cast in combat.\n\r", ch );
			pop_call();
			return TRUE;
		}
	}

	if (victim == NULL)
	{
		pop_call();
		return FALSE;
	}

	if (IS_OFFENSIVE(victim, sn) && !IS_NPC(ch) && !IS_NPC(victim) && IS_SET(ch->in_room->area->flags, AFLAG_NO_PVP))
	{
		send_to_char( "You are currently in a no-pvp area.\n\r", ch);
		pop_call();
		return TRUE;
	}
		
	// can't attack the supermob!
	if (victim == supermob)
	{
		pop_call();
		return TRUE;
	}

	if (is_affected(victim, gsn_time_stop))
	{
		act("You cannot affect $N while $E is frozen in time.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return TRUE;
	}

	if (victim == ch)
	{
		pop_call();
		return FALSE;
	}
	
	if (IS_SET(skill_table[sn].flags, SF_NONCOMBAT))
	{
		if (in_combat(victim))
		{
			send_to_char( "This effect cannot be cast in combat.\n\r", ch );
			pop_call();
			return TRUE;
		}
	}

	//hostile action removes fascination.
	if (IS_OFFENSIVE(victim, sn))
	{
		if (IS_AFFECTED(victim, AFF2_FASCINATED))
		{
			AFFECT_STRIP(victim, AFF2_FASCINATED);
			act("Your fascination is broken due to $N's actions.", victim, NULL, ch, TO_CHAR);
		}
		// victim's safe room must be taken into account as well
		if (IS_SET(victim->in_room->room_flags, ROOM_SAFE))
		{
			send_to_char( "Wards prevent you from doing that here.\n\r", ch);
			pop_call();
			return TRUE;
		}
		// even an offensive spell can't be targeted against a sancted victim
		// which is why this isn't in can_melee_attack instead
		if (IS_AFFECTED(victim, AFF_SANCTUARY) && !IS_AREA_SPELL(sn))
		{
			if (!save_resist(ch, victim, gsn_sanctuary, get_affect_level(victim, gsn_sanctuary)))
			{
				send_to_char( "A magical force stays your hand.\n\r", ch);
				pop_call();
				return TRUE;
			}
		}	
	}

	if ((is_spell(sn) || IS_SET(skill_table[sn].flags, SF_SUPERNATURAL|SF_SPELL_LIKE)) && victim->in_room && IS_SET(victim->in_room->room_flags, ROOM_NO_MAGIC))
	{
		send_to_char( "Nothing happens...\n\r",ch);
		pop_call();
		return TRUE;
	}

	if (who_fighting(ch) && who_fighting(ch) == victim)
	{
		pop_call();
		return FALSE;
	}

	if (ch->in_room->area->low_r_vnum == ROOM_VNUM_ARENA)
	{
		pop_call();
		return FALSE;
	}

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

		case TAR_CHAR_OFFENSIVE:
		case TAR_OBJ_CHAR_OFF:
			if (ch->desc)
			{
				if (!IS_NPC(victim) && !check_murder(ch, victim))
				{
					pop_call();
					return TRUE;
				}
			}
			if (!IS_NPC(ch))
			{
				ch->pcdata->just_died_ctr = 0;
			}
			break;

		case TAR_UNDEAD_OFF:
			if (IS_UNDEAD(victim))
			{
				if (ch->desc)
				{
					if (!IS_NPC(victim) && !check_murder(ch, victim))
					{
						pop_call();
						return TRUE;
					}
				}
				if (!IS_NPC(ch))
				{
					ch->pcdata->just_died_ctr = 0;
				}
				break;
			}
			break;

		case TAR_UNDEAD_DEF:
			if (!IS_UNDEAD(victim))
			{
				if (ch->desc)
				{
					if (!IS_NPC(victim) && !check_murder(ch, victim))
					{
						pop_call();
						return TRUE;
					}
				}
				if (!IS_NPC(ch))
				{
					ch->pcdata->just_died_ctr = 0;
				}
				break;
			}
			break;

		case TAR_CHAR_DEFENSIVE:
		case TAR_OBJ_CHAR_DEF:
			break;
	}
	pop_call();
	return FALSE;
}

/*
	true if victim is a legal target for a mass attack - Scandum 01/04/2002
*/

bool can_mass_cast( CHAR_DATA *ch, CHAR_DATA *victim, int sn )
{
	push_call("can_mass_cast(%p,%p)",ch,victim);
	CHAR_DATA *rch, *rch_next;

	if (victim == ch)
	{
		pop_call();
		return FALSE;
	}
	
	if (IS_SET(skill_table[sn].flags, SF_NONCOMBAT))
	{
		if (in_combat(victim))
		{
			pop_call();
			return FALSE;
		}
	}

	if (!IS_NPC(ch) && !IS_NPC(victim) && IS_SET(ch->in_room->area->flags, AFLAG_NO_PVP))
	{
		pop_call();
		return FALSE;
	}
		
	if (who_fighting(victim))
	{
		if (who_fighting(victim) == ch)
		{
			pop_call();
			return TRUE;
		}
		if (!is_same_group(ch, who_fighting(victim)))
		{
			pop_call();
			return FALSE;
		}
	}

	if (IS_NPC(victim) && IS_SET(victim->act, ACT_MOBINVIS))
	{
		pop_call();
		return FALSE;
	}

	if (is_same_group(ch, victim))
	{
		pop_call();
		return FALSE;
	}

	if (IS_NPC(victim) && (IS_AFFECTED(victim, AFF_DOMINATE) || IS_SET(ch->act, ACT_PET)) && victim->master != NULL)
	{
		if (victim->master == ch)
		{
			pop_call();
			return FALSE;
		}
		for (rch = ch->in_room->first_person ; rch ; rch = rch_next)
		{
			rch_next = rch->next_in_room;
			
			if (victim->master == rch)
			{
				pop_call();
				return TRUE;
			}
		}
	}
		
	if (ch->in_room->area->low_r_vnum == ROOM_VNUM_ARENA)
	{
		pop_call();
		return TRUE;
	}

	pop_call();
	return TRUE;
}


int get_mana( CHAR_DATA *ch, int sn, int circle, int class  )
{
	int mana = 0, effcircle, maxcircle, diff;

	push_call("get_mana(%p)", circle);

	effcircle = get_metamagic(ch, sn, ch->metamagic, circle, class, FALSE);

	switch (effcircle)
	{
		case 0:
			break;
		case 1:
			mana = 1;
			break;
		case 2:
			mana = 3;
			break;
		case 3:
			mana = 5;
			break;
		case 4:
			mana = 7;
			break;
		case 5:
			mana = 9;
			break;
		case 6:
			mana = 11;
			break;
		case 7:
			mana = 13;
			break;
		case 8:
			mana = 15;
			break;
		case 9:
			mana = 17;
			break;
		default:
			bug("get_mana: bad spell circle!",0);
			break;
	}
	
	// no additional mana for scaling if the spell isn't scalable
	if (IS_SET(skill_table[sn].flags, SF_SCALABLE))
	{
		maxcircle = UMIN(max_spell_circle(ch, class), skill_table[sn].mana_move);
		diff = UMAX(0, maxcircle - circle);
		
		mana += diff * 2;
	}
		
	wiz_printf("get_mana: effective circle = %d, mana cost = %d.", effcircle, mana);

	pop_call();
	return(mana);
}


bool is_spell_like( int sn )
{
	push_call("is_spell(%p)",sn);

	if (sn > 0 && sn < TYPE_HIT && is_string(skill_table[sn].name) && (skill_table[sn].skilltype == FSKILL_SPELL || IS_SET(skill_table[sn].flags, SF_SPELL_LIKE)))
	{
		pop_call();
  	return TRUE;
  }

	pop_call();
	return FALSE;
}


bool is_spell( int sn )
{
	push_call("is_spell(%p)",sn);

	if (sn > 0 && sn < TYPE_HIT && is_string(skill_table[sn].name) && skill_table[sn].skilltype == FSKILL_SPELL)
	{
		pop_call();
  	return TRUE;
  }

	pop_call();
	return FALSE;
}


/*
 * d20 counterspelling w/ imp. counterspell support - Kregor
 * Variant rule in play here as follows:
 * Countering is an immediate action, so long as you haven't
 * acted already in the round.
 * Any attempt to counter a spell requires a spellcraft check
 * opposed by a caster level check, both rolls modified by their 
 * respective spells' circle.
 *
 * 3/23/11 added Countersong ability support.
 */
bool counterspell(CHAR_DATA *ch, CHAR_DATA *caster, int sn, int level, bool CounterSong)
{
	int snb, mana, class, circle, circleb, chroll, castroll;
	
	push_call("counterspell(%p,%p,%p)",caster,ch,sn,level);
	
	/* is immediate action so long as standard action hasn't been taken this round */
	if (IS_SET(ch->action, ACTION_STANDARD))
	{
		act( "You've already acted in this round.",  ch, NULL, caster, TO_CHAR);
		if (IS_NPC(ch))
			wiz_printf("counterspell(%d - %s): can't take an immediate action.", ch->pIndexData->vnum, ch->name);
		pop_call();
		return FALSE;
	}
	/* little checks so NPCs don't counter non-harmful spells */
	if (IS_NPC(ch))
	{
		switch (skill_table[sn].target)
		{
			default:
				pop_call();
				return FALSE;
			case TAR_CHAR_OFFENSIVE:
			case TAR_OBJ_CHAR_OFF:
				break;
			case TAR_UNDEAD_OFF:
				if (!IS_UNDEAD(ch))
				{
					pop_call();
					return FALSE;
				}
				break;
		}
	}
	
	circle = get_spell_circle(caster, sn);
	castroll = dice(1,20) + circle + level;
	
	if (learned(caster, gsn_furtive_spell) && !learned(ch, gsn_shadow_casting))
	{
		castroll += 4;
	}

	if (CounterSong)
	{
		if (!CHECK_USES(ch, gsn_bardic_song))
		{
			act("You have no bardic music left.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (!IS_SET(skill_table[sn].flags, SF_AUDIBLE|SF_VERBAL))
		{
			act("That spell has no verbal or audible component.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		chroll = perform_roll(ch);
		ch->uses[gsn_bardic_song]++;
	}
	else
	{
		/* First, without improved counterspell, must use same spell or dispel magic */
		if (!learned(ch, gsn_imp_counterspell))
		{
			if ((circleb = get_spell_circle(ch, sn)) == -1)
			{
				if ((circleb = get_spell_circle(ch, gsn_dispel_magic)) == -1)
				{
					if ((circleb = get_spell_circle(ch, gsn_greater_dispel)) == -1)
					{
						act( "You do not have a spell ready to counter $N's.",  ch, NULL, caster, TO_CHAR);
						if (IS_NPC(ch))
							wiz_printf("counterspell(%d - %s): tried to counter without imp. counterspell.", ch->pIndexData->vnum, ch->name);
						pop_call();
						return FALSE;
					}
					else
						snb = gsn_greater_dispel;
				}
				else
					snb = gsn_dispel_magic;
			}
			else
				snb = sn;
		}
		/* next, look for a spell of the same school and at least the same level */
		else
		{
			for (snb = 0 ; *skill_table[snb].name != '\0' ; snb++)
			{
				if (!is_spell(snb))
					continue;
				
				if (skill_table[sn].spell_school != skill_table[snb].spell_school)
					continue;
	
				if ((circleb = get_spell_circle(ch, snb)) >= circle)
					break;
			}
			if (*skill_table[snb].name != '\0')
			{
				act( "You do not have a suitable counter to $N's spell.",  ch, NULL, caster, TO_CHAR);
				if (IS_NPC(ch))
					wiz_printf("counterspell(%d - %s): tried to counter with imp. counterspell and failed.", ch->pIndexData->vnum, ch->name);
				pop_call();
				return FALSE;
			}
		}
		if ((class = prepared(ch, snb)) == -1)
		{
			if ((class = spontaneous_cast(ch, snb)) == -1)
			{
				act( "You are not powerful enough to counter $N's spell.",  ch, NULL, caster, TO_CHAR);
				pop_call();
				return FALSE;
			}
		}
		/* If you haven't got the mana, you can't counter */
		if ((mana = get_mana(ch, snb, circleb, class)) > ch->mana[class])
		{
			act( "You lack the mana to counter $N's spell.",  ch, NULL, caster, TO_CHAR);
			if (IS_NPC(ch))
				wiz_printf("counterspell(%d - %s): tried to counter without enough mana.", ch->pIndexData->vnum, ch->name);
			pop_call();
			return FALSE;
		}
		
		chroll = spellcraft_roll(ch) + circleb;
		
		ch->mana[class] -= mana;
	}
	
	if (chroll < castroll)
	{
		if (CounterSong
		|| !arcane_mastery(ch, SCHOOL_ABJURATION)
		|| spellcraft_roll(ch) + circleb < castroll)
		{
			act( "{168}$n tries to counter $N's spell and fails.", ch, NULL, caster, TO_NOTVICT);
			act( "{168}$n tries to counter your spell and fails.",  ch, NULL, caster, TO_VICT);
			act( "{168}You try to counter $N's spell and fails.",  ch, NULL, caster, TO_CHAR);

			TAKE_ACTION(ch, ACTION_STANDARD);
			pop_call();
			return FALSE;
		}
	}

	act( "{168}$N's magic dissipates as $n counters it.", ch, NULL, caster, TO_NOTVICT);
	act( "{168}Your spell dissipates as $n counters it.",  ch, NULL, caster, TO_VICT);
	act( "{168}$N's spell dissipates as you counter it.",  ch, NULL, caster, TO_CHAR);
	
	TAKE_ACTION(ch, ACTION_STANDARD);

	pop_call();
	return TRUE;
}


void do_counterspell( CHAR_DATA *ch, char *argument )
{
	CHAR_DATA *caster;

	push_call("do_counterspell(%p,%p)",ch,argument);
	
	if (IS_SET(ch->action, ACTION_STANDARD))
	{
		act("You have already made a standard action this round.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	if (*argument == '\0')
	{
		act("Counterspell whom?", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	if ((caster = get_char_room(ch, argument)) == NULL)
	{
		act("There is no $t here.", ch, argument, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	if (!caster->casting || !is_spell(caster->cast_sn))
	{
		act("$N is not casting a spell.", ch, argument, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	if (counterspell(ch, caster, caster->cast_sn, caster->cast_level, FALSE))
	{
		free_cast(caster);
	}

	pop_call();
	return;
}
	
	

ROOM_INDEX_DATA *get_rift_room( CHAR_DATA *ch, int rift_number)
{
	ROOM_INDEX_DATA *rift_room;

	push_call("get_rift_room(%p,%p)",ch,rift_number);

	srand(value + ch->pcdata->pvnum + 1);

	while (TRUE)
	{
		rift_room = get_room_index(rand() % 20000);

		if (rift_room == NULL)
		{
			continue;
		}
		if (IS_SET(rift_room->room_flags, ROOM_SAFE))
		{
			continue;
		}
		if (IS_SET(rift_room->room_flags, ROOM_CLANHALL))
		{
			continue;
		}
		if (IS_SET(rift_room->room_flags, ROOM_PET_SHOP))
		{
			continue;
		}
		if (IS_SET(rift_room->room_flags, ROOM_PRIVATE))
		{
			continue;
		}
		if (IS_SET(rift_room->room_flags, ROOM_SOLITARY))
		{
			continue;
		}
		if (IS_SET(rift_room->room_flags, ROOM_NO_RECALL))
		{
			continue;
		}
		if (rift_room->sector_type == SECT_ETHEREAL)
		{
			continue;
		}
		if (rift_room->sector_type == SECT_ASTRAL)
		{
			continue;
		}
		if (IS_SET(rift_room->area->flags, AFLAG_NOTELEPORT))
		{
			continue;
		}
		if (IS_SET(rift_room->area->flags, AFLAG_NORECALL))
		{
			continue;
		}
		if (IS_SET(rift_room->area->flags, AFLAG_NORIP))
		{
			continue;
		}
		if (!IS_IMMORTAL(ch))
		{
			if (ch->level < rift_room->area->low_hard_range)
			{
				continue;
			}
			if (ch->level > rift_room->area->hi_hard_range)
			{
				continue;
			}
		}
		break;
	}
	pop_call();
	return rift_room;
}


/*
 * Global save/resist check for all spells.
 * Uses target mode of spell to determine whether save or resistance applies.
 * Returns TRUE if completely resisted, PARTIAL if partially saved or
 * FALSE if save and resistance failed.
 */
int save_resist(CHAR_DATA *ch, CHAR_DATA *victim, int sn, int level)
{
	char name[MAX_INPUT_LENGTH];

	bool area = FALSE;
	bool fort = FALSE;
	bool refl = FALSE;
	bool will = FALSE;
	int save = skill_table[sn].save;

	push_call("save_resist(%p)",ch);

	// If you REALLY want to cast that on yourself... - Kregor
	if (ch == victim)
	{
		pop_call();
		return FALSE;
	}

	// invulnerable to any affect while time stopped - Kregor
	if (is_affected(victim, gsn_time_stop))
	{
		pop_call();
		return TRUE;
	}

	//ethereal creatures cannot be affected by non-ethereal except for force affects & abjurations.
	if (!CAN_ETHEREAL_WALK(ch) && CAN_ETHEREAL_WALK(victim))
	{
		if (skill_table[sn].spell_school != SCHOOL_ABJURATION && !IS_SET(skill_table[sn].spell_desc, SDESC_FORCE))
		{
			act("Only force affects and abjurations can affect ethereal creatures.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	}

	if (!CAN_ETHEREAL_WALK(victim) && CAN_ETHEREAL_WALK(ch))
	{
		act("Your magic has no affect upon the material world.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (skill_table[sn].name)
		strcpy(name, skill_table[sn].name);
	else
		strcpy(name, "spell");
		
	switch (save)
	{
		case SAVE_FORT_HALF:
		case SAVE_FORT_NONE:
			fort = TRUE;
			break;
		case SAVE_REFL_HALF:
		case SAVE_REFL_NONE:
			refl = TRUE;
			break;
		case SAVE_WILL_HALF:
		case SAVE_WILL_NONE:
			will = TRUE;
			break;
	}
	
	if (IS_AREA_SPELL(sn))
		area = TRUE;
		
	if (IS_SET(skill_table[sn].flags, SF_VISUAL) && IS_BLIND(victim))
	{
		act("$N can't seem to see your magic to fully appreciate it.", ch, NULL, victim, TO_CHAR);
		act("$N can't seem to see $n's magic to fully appreciate it.", ch, NULL, victim, TO_NOTVICT);
		pop_call();
		return TRUE;
	}

	if (race_type(victim) == RTYPE_CONSTRUCT)
	{
		if (IS_SET(skill_table[sn].spell_desc, SDESC_HEALING|SDESC_NEGATIVE))
		{
			act("$N is unaffected by your magic.", ch, NULL, victim, TO_CHAR);
			act("$N seems to be unaffected.", ch, NULL, victim, TO_NOTVICT);
			pop_call();
			return TRUE;
		}
	}

	if (domain_apotheosis(victim, DOMAIN_REPOSE))
	{
		if (sn == gsn_enervation || sn == gsn_energy_drain)
		{
			act("$N is unaffected by your magic.", ch, NULL, victim, TO_CHAR);
			act("$N seems to be unaffected.", ch, NULL, victim, TO_NOTVICT);
			pop_call();
			return TRUE;
		}
	}

	if (skill_table[sn].skilltype == FSKILL_BARDSONG
	&& is_affected(victim, gsn_bardic_song))
	{
		act("$N is already affected by bardic music.", ch, NULL, victim, TO_CHAR);
		act("You are already under the affect of bardic music.", ch, NULL, victim, TO_VICT);
		pop_call();
		return TRUE;
	}

	if (IS_DEAF(victim) || IS_AFFECTED(victim, AFF2_SILENCE))
	{
		if (IS_SET(skill_table[sn].flags, SF_AUDIBLE|SF_LANGUAGE)
		|| skill_table[sn].skilltype == FSKILL_BARDSONG
		|| sn == gsn_bardic_song)
		{
			act("$N is unaffected by your spell.", ch, NULL, victim, TO_CHAR);
			act("$N is unaffected by $n's spell.", ch, NULL, victim, TO_NOTVICT);
			act("You are unaffected by $n's spell.", ch, NULL, victim, TO_VICT);
			pop_call();
			return TRUE;
		}
	}
	
	if (IS_SET(skill_table[sn].spell_desc, SDESC_SONIC)
	&& IS_AFFECTED(victim, AFF2_SILENCE))
	{
		act("$N is unaffected by your spell.", ch, NULL, victim, TO_CHAR);
		act("$N is unaffected by $n's spell.", ch, NULL, victim, TO_NOTVICT);
		act("You are unaffected by $n's spell.", ch, NULL, victim, TO_VICT);
		pop_call();
		return TRUE;
	}
	
	if (IS_SET(skill_table[sn].flags, SF_LANGUAGE)
	&& !can_understand(victim, ch, TRUE))
	{
		act("$N cannnot understand your incantation.", ch, NULL, victim, TO_CHAR);
		act("$n utters words you cannot understand.", ch, NULL, victim, TO_VICT);
		pop_call();
		return TRUE;
	}
	
	if (IS_SET(skill_table[sn].flags, SF_RANGED_TOUCH))
	{
		if (!check_hit(ch, victim, dice(1,20), 0, sn, NULL, TRUE, TRUE))
		{
			act( "Your $t misses $N.", ch, name, victim, TO_CHAR);
			act( "$n's $t misses you.", ch, name, victim, TO_VICT);
			act( "$n misses $N with $s $t.", ch, name, victim, TO_NOTVICT);
			pop_call();
			return TRUE;
		}
	}
	
	//Adding globes of invuln, resists anything unless cast on self - Kregor
	if (is_affected(victim, gsn_globe_of_invulnerability))
	{
		if (get_spell_circle(ch, sn) <= 4)
		{
			act("$N's shimmering aura absorbs your spell!", ch, NULL, victim, TO_CHAR);
			act("$N's shimmering aura absorbs $n's spell!", ch, NULL, victim, TO_NOTVICT);
			act("Your globe of invulnerability absorbs $n's spell!", ch, NULL, victim, TO_VICT);
			pop_call();
			return TRUE;
		}
	}
	else if (is_affected(victim, gsn_minor_globe))
	{
		if (get_spell_circle(ch, sn) <= 3)
		{
			act("$N's shimmering aura absorbs your spell!", ch, NULL, victim, TO_CHAR);
			act("$N's shimmering aura absorbs $n's spell!", ch, NULL, victim, TO_NOTVICT);
			act("Your globe of invulnerability absorbs $n's spell!", ch, NULL, victim, TO_VICT);
			pop_call();
			return TRUE;
		}
	}

	if (skill_table[sn].spell_school == SCHOOL_DIVINATION && check_nondetection(ch, victim, sn))
	{
		act("N's mind is closed off to you!", ch, NULL, victim, TO_CHAR);
		pop_call();
		return TRUE;
	}
		
	if (IS_OFFENSIVE(victim, sn))
	{
		if (area && !can_mass_cast(ch, victim, sn))
		{
			pop_call();
			return TRUE;
		}
		
		if (check_spell_resist(victim, ch, sn, level))
		{
			pop_call();
			return TRUE;
		}
		
		if (sn == gsn_undeath_ward && IS_UNDEAD(ch) && ch->level < level)
		{
			pop_call();
			return FALSE;
		}
		
		if (save != SAVE_NONE && is_immune(victim, skill_table[sn].spell_desc))
		{
			pop_call();
			return TRUE;
		}
		
		if (sn == gsn_magic_missile && is_affected(victim, gsn_shield))
		{
			act( "$n's magic missile dissipates as it reaches you!", ch, name, victim, TO_VICT);
			act( "$n's magic missile dissipates as it reaches $N!", ch, name, victim, TO_NOTVICT);
			act( "Your magic missile dissipates as it reaches $N!", ch, name, victim, TO_CHAR);
			pop_call();
			return TRUE;
		}
		if (refl)
		{
			if (refl_save(victim, ch, level, sn))
			{
				if (save == SAVE_REFL_NONE || learned(ch, gsn_evasion))
				{
					act( "You dodge aside from $n's $t.", ch, name, victim, TO_VICT);
					act( "$N dodges aside from $n's $t.", ch, name, victim, TO_NOTVICT);
					act( "$N dodges aside from your $t.", ch, name, victim, TO_CHAR);
					pop_call();
					return TRUE;
				}
				else
				{
					act( "You dodge the brunt of $n's $t.", ch, name, victim, TO_VICT);
					act( "$N dodges the brunt of $n's $t.", ch, name, victim, TO_NOTVICT);
					act( "$N dodges the brunt of your $t.", ch, name, victim, TO_CHAR);
					pop_call();
					return PARTIAL;
				}
			}
			else
			{
				if (learned(ch, gsn_imp_evasion))
				{
					act( "{138}You evade the brunt of $n's $t.", ch, name, victim, TO_VICT);
					act( "{138}$N evades the brunt of $n's $t.", ch, name, victim, TO_NOTVICT);
					act( "{138}$N evades the brunt of your $t.", ch, name, victim, TO_CHAR);
					pop_call();
					return PARTIAL;
				}
			}
		}
		else if (fort && fort_save(victim, ch, level, sn))
		{
			if (save == SAVE_FORT_NONE || (learned(ch, gsn_mettle) && IS_AWAKE(ch)))
			{
				act( "You resist $n's $t.", ch, name, victim, TO_VICT);
				act( "$N resists $n's $t.", ch, name, victim, TO_NOTVICT);
				act( "$N resists your $t.", ch, name, victim, TO_CHAR);
				pop_call();
				return TRUE;
			}
			else
			{
				act( "You partially resist $n's $t.", ch, name, victim, TO_VICT);
				act( "$N partially resists $n's $t.", ch, name, victim, TO_NOTVICT);
				act( "$N partially resists your $t.", ch, name, victim, TO_CHAR);
				pop_call();
				return PARTIAL;
			}
		}
		else if (will && will_save(victim, ch, level, sn))
		{
			if (save == SAVE_WILL_NONE || (learned(ch, gsn_mettle) && IS_AWAKE(ch)))
			{
				act( "You resist $n's $t.", ch, name, victim, TO_VICT);
				act( "$N resists $n's $t.", ch, name, victim, TO_NOTVICT);
				act( "$N resists your $t.", ch, name, victim, TO_CHAR);
				pop_call();
				return TRUE;
			}
			else
			{
				act( "You partially resist $n's $t.", ch, name, victim, TO_VICT);
				act( "$N partially resists $n's $t.", ch, name, victim, TO_NOTVICT);
				act( "$N partially resists your $t.", ch, name, victim, TO_CHAR);
				pop_call();
				return PARTIAL;
			}
		}
	}
	pop_call();
	return FALSE;
}


/*
 * Wrapping spells into universal functions - Kregor 3/10/08
 */

/*
 * All standard damage spells should go thru this function
 */
void spell_damage(CHAR_DATA *ch, CHAR_DATA *victim, int dmg, int sn, int level)
{
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	ROOM_TIMER_DATA *rtd;
	int dam, save;
	bool area = FALSE;

	push_call("spell_damage(%p)",ch);
	
	if (IS_AREA_SPELL(sn))
	{
		area = TRUE;
		
		// fire attacks remove swarms from the room - Kregor
		if (IS_SET(skill_table[sn].spell_desc, SDESC_FIRE)
		&& (rtd = get_room_affect(victim->in_room, ROOM_SWARM)) != NULL)
		{
			rtd->duration = 0;
		}
	}
	
	if (IS_SET(skill_table[sn].flags, SF_RANGED_TOUCH))
	{
		if (!check_hit(ch, victim, dice(1,20), 0, sn, NULL, TRUE, TRUE))
		{
			damage( ch, victim, 0, sn, NULL );
			pop_call();
			return;
		}
	}
	if (IS_SET(skill_table[sn].flags, SF_RANGED_TOUCH|SF_TOUCH))
	{
		if (can_backstab(ch, victim, sn) && number_percent() > get_apply(victim, APPLY_FORTIFICATION))
		{
			int bs_dice = ROUNDUP(multi_skill_level(ch, gsn_backstab) / 2);
			int bs_dam = 0;
	
			if (get_monk_style(ch) == STYLE_SLEEPING_TIGER && class_level(ch, CLASS_MONK) >= 12)
				bs_dice++;
	
			if (bs_dice > 0)
			{
				if (learned(ch, gsn_greater_sneak_attack))
				{
					bs_dam = dice(bs_dice, 8);
				}
				else
				{
					bs_dam = dice(bs_dice, 6);
				}
			}
			dmg += bs_dam;
		}
	}

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		dam = dmg;
		
		if (area && !can_mass_cast(ch, vch, sn))
			continue;
			
		if (!area && vch != victim)
			continue;
			
		if (area && IS_NPC(ch) && !is_same_group(victim, vch))
			continue;

		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
		{
			continue;
		}
		
		if (save == PARTIAL)
		{
			switch (skill_table[sn].save)
			{
				case SAVE_FORT_QUARTER:
				case SAVE_REFL_QUARTER:
				case SAVE_WILL_QUARTER:
					dam /= 4;
					break;
				case SAVE_FORT_NONE:
				case SAVE_REFL_NONE:
				case SAVE_WILL_NONE:
					dam = 0;
					break;
				default:
					dam /= 2;
					break;
			}
		}

		// side effects go before damage
		if (sn == gsn_shout && !save)
		{
			af.type      = sn;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.duration  = ROUNDUP(level/5);
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_DEAF;
			af.level     = level;
			affect_to_char( ch, vch, &af );
		}			

		if (sn == gsn_flamestrike)
		{
			dam /= 2;
			damage( ch, vch, dam, gsn_divine_hit, NULL );
		}
		if (sn == gsn_ice_storm)
		{
			dam /= 2;
			damage( ch, vch, dam, gsn_bash_hit, NULL );
		}
		damage( ch, vch, dam, sn, NULL );
		
		/* end this loop if victim only */
		if (!area)
			break;
	}
	pop_call();
	return;
}


void show_identify_string( OBJ_DATA *obj, CHAR_DATA *viewer)
{
	char buf[MAX_STRING_LENGTH];
	char buf2[MAX_STRING_LENGTH];
	AFFECT_DATA *paf;
	int cnt;

	push_call("show_identify_string(%p,%p)",obj,viewer);

	buf2[0] = '\0';
		
	if (is_string(obj->id_name))
		cat_sprintf(buf2, "%s\n\r", obj->id_name);
	if (is_string(obj->id_descr))
		cat_sprintf(buf2, "%s\n\r", obj->id_descr);

	if (IS_OBJ_STAT(obj, ITEM_MAGIC))
	{
		cat_sprintf(buf2, "This item does have magical properties.\n\r");
	}
	else
	{
		cat_sprintf(buf2, "This item does not have any magical properties.\n\r");
		send_to_char_color(buf2, viewer);
		pop_call();
		return;
	}
	
	switch ( obj->item_type )
	{
		case ITEM_SCROLL:
			sprintf(buf, "It contains a level %d %s spell of:",
				obj->value[0], obj->value[2] < 1 ? "arcane" : "divine");
			cat_sprintf(buf, " '%s'", skill_name(obj->value[1]));
			cat_sprintf(buf2, "%s\n\r", buf);
			break;

		case ITEM_POTION:
			sprintf(buf, "It contains a level %d spell of:", obj->value[0]);
			cat_sprintf(buf, " '%s'", skill_name(obj->value[1]));
			cat_sprintf(buf2, "%s\n\r", buf);
			break;

		case ITEM_PILL:
			sprintf(buf, "It contains level %d spells of:", obj->value[0]);

			if (valid_skill(obj->value[1]))
			{
				cat_sprintf(buf, " '%s'", skill_table[obj->value[1]].name);
			}
			if (valid_skill(obj->value[2]))
			{
				cat_sprintf(buf, " '%s'", skill_table[obj->value[2]].name);
			}
			if (valid_skill(obj->value[3]))
			{
				cat_sprintf(buf, " '%s'", skill_table[obj->value[3]].name);
			}
			cat_sprintf(buf2, "%s\n\r", buf);
			break;

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

			if (valid_skill(obj->value[3]))
			{
				cat_sprintf(buf, " '%s'", skill_table[obj->value[3]].name);
			}
			cat_sprintf(buf2, "%s\n\r", buf);
			break;

		case ITEM_WEAPON:
		case ITEM_AMMO:
			for (cnt = 0 ; cnt < 29 ; cnt++)
			{
				if (IS_SET(obj->value[1], 1 << cnt))
				{
					cat_sprintf(buf2, "%s: %s", weapon_flags[cnt], weapon_flag_descs[cnt]);
					
					if (1 << cnt == WFLAG_BANE || 1 << cnt == WFLAG_SLAYING)
					{
						if (obj->value[3])
						{
							cat_sprintf(buf2, " %s %s", flag_string(obj->value[3], race_specs), type_string(obj->value[2], rtype_flags));
						}
						else
						{	
							cat_sprintf(buf2, " %s", type_string(obj->value[2], rtype_flags));
						}
					}
					
					cat_sprintf(buf2, ".\n\r");
				}
			}
			break;

		case ITEM_ARMOR:
			cat_sprintf(buf2, "Its armor bonus is %d.\n\r", armor_table[obj->value[0]].ac_bonus );
			break;

		case ITEM_CONTAINER:
			if (IS_SET(obj->value[1], CONT_HOLDING))
			{
				cat_sprintf(buf2, "Bag of holding: holds %d lbs. for only %s lbs weight.\n\r", obj->value[0]/10, ROUNDUP(obj->value[0]/160));
			}
			break;
	}

	for (paf = obj->first_affect ; paf != NULL ; paf = paf->next)
	{
		switch (paf->location)
		{
			case APPLY_NONE:
				break;

			default:
				cat_sprintf(buf2, "Affects %s by %d.\n\r", affect_loc_name(paf->location), paf->modifier);
				break;
		}
		switch (paf->bittype)
		{
			case AFFECT_TO_NONE:
				break;

			case AFFECT_TO_CHAR:
				if (paf->bitvector < 0)
					cat_sprintf(buf2, "Affects wearer with %s.\n\r", flag_string(paf->bitvector, a2_flags));
				else
					cat_sprintf(buf2, "Affects wearer with %s.\n\r", flag_string(paf->bitvector, a_flags));
				break;

			case AFFECT_TO_OBJ:
				cat_sprintf(buf2, "Affects object with %s.\n\r", flag_string(paf->bitvector, o_flags));
				break;
		}
	}
	send_to_char_color(buf2, viewer);
	pop_call();
	return;
}

/*
 * Automatic actions for irresistible dance - Kregor 2/10/12
 */
void irresistible_dance(CHAR_DATA * ch)
{
	CHAR_DATA *vch = NULL;

	push_call("irresistible_dance(%p)",ch);

	if (!IS_AWAKE(ch))
	{
		TAKE_ACTION(ch, ACTION_FULL);
		pop_call();
		return;
	}
	
	// select random dance partner!
	for (vch = ch->in_room->first_person ; vch ; vch = vch->next)
	{
		if (number_bits(1) == 0)
		{
			break;
		}
	}
	
	switch (number_range(1,4))
	{
		case 1:
			act("{178}$n skips and prances to the music in $s head.", ch, NULL, NULL, TO_ROOM);
			act("{178}You dance and skip to the music in your head.", ch, NULL, NULL, TO_CHAR);
			break;
		case 2:
			if (vch != NULL)
			{
				act("{178}$n sweeps $N up into a romantic waltz!", ch, NULL, vch, TO_NOTVICT);
				act("{178}You sweep $N up into a romantic waltz!", ch, NULL, vch, TO_CHAR);
				act("{178}$n sweeps you up into a romantic waltz!", ch, NULL, vch, TO_CHAR);
			}
			else
			{
				act("{178}You dance a merry jig!", ch, NULL, NULL, TO_CHAR);
				act("{178}$n dances a merry jig!", ch, NULL, NULL, TO_ROOM);
			}
			break;
		case 3:
			act("{178}You spin about in a graceful pirouette!", ch, NULL, NULL, TO_CHAR);
			act("{178}$n spins about in a graceful pirouette!", ch, NULL, NULL, TO_ROOM);
			break;
		case 4:
			act("{178}You mock a dance of the veils!", ch, NULL, NULL, TO_CHAR);
			act("{178}$n mocks a dance of the veils!", ch, NULL, NULL, TO_ROOM);
			break;
	}
	
	// draw AoO from hostiles
	for (vch = ch->in_room->first_person ; vch ; vch = vch->next)
	{
		if (who_fighting(vch) && who_fighting(vch) == ch)
		{
			attack_of_opportunity(vch, ch);
		}
	}

	TAKE_ACTION(ch, ACTION_FULL);

	pop_call();
	return;
}

/*
 * Separated recall function from do_ function
 * to allow use for many sources - Kregor
 */
bool recall( CHAR_DATA *ch, int vnum )
{
	ROOM_INDEX_DATA *old_room;
	CHAR_DATA *rch, *rch_next;

	if (IS_NPC(ch))
	{
		pop_call();
		return FALSE;
	}

	if (!is_room_good_for_teleport(ch, ch->in_room->vnum)
	|| !is_room_good_for_teleport(ch, vnum))
	{
		act("You cannot reach that destination.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	old_room = ch->in_room;

	if (ch->in_room->vnum == vnum)
	{
		send_to_char("You are already in your recall spot.\n\r", ch);
		pop_call();
		return FALSE;
	}

	act( "You pray for refuge!", ch, NULL, NULL, TO_CHAR );
	act( "$n prays for refuge!", ch, NULL, NULL, TO_ROOM );

	if (in_combat(ch))
	{
		int lose = 99 + ch->level * ch->level * 10;
		gain_exp(ch, 0 - lose);

		ch_printf_color( ch, "{138}You recall from combat! You lose %d experience points.\n\r", lose );
	}

	act( "$n disappears.", ch, NULL, NULL, TO_ROOM);

	if (in_combat(ch))
		withdraw_combat(ch);

	char_to_room(ch, vnum, TRUE);

	act( "$n appears in the room.", ch, NULL, NULL, TO_ROOM );

	do_look(ch, "auto");

	/*
		Pets recall with char    Chaos 12/6/93
	*/
	for (rch = old_room->first_person ; rch ; rch = rch_next)
	{
		rch_next = rch->next_in_room;

	 	if (is_master(rch, ch))
	 	{
	 		act( "$n disappears.", rch, NULL, NULL, TO_ROOM);
	 		char_to_room(rch, vnum, TRUE);
	 		act( "$n appears in the room.", rch, NULL, NULL, TO_ROOM);
		}
	}
	if (!IS_NPC(ch))
	{
		mprog_greet_trigger(ch);
		oprog_greet_trigger(ch);
		rprog_greet_trigger(ch);
	}	
	pop_call();
	return TRUE;
}

/*
 * Consolidated support function
 * for all shapechanging functions - Kregor 11/15/10
 */
bool morph( CHAR_DATA *ch, CHAR_DATA *victim, int race, int sn, int level )
{
	OBJ_DATA *obj;
	AFFECT_DATA af;
	int duration = -1;

	push_call("morph(%p,%p,%p,%p)",ch,victim,race,level);
	
	if (!is_string(race_table[race].race_name))
	{
		bug("morph: invalid race no.",0);
		pop_call();
		return FALSE;
	}
	
	if (IS_SET(race_table[victim->race].flags, RSPEC_INCORPOREAL))
	{
		act("Incorporeal beings cannot be shapechanged.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
		
	revert_morph(ch, FALSE);
	
	if (sn != gsn_baleful_polymorph)
		duration = turn * level;
	else
		duration = -1;
	
	af.type      = sn;
	af.duration  = duration;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = AFF_NONE;
	af.level     = level;

	af.location  = APPLY_STR;
	af.modifier  = UMIN(race_table[race].race_mod[0] - race_table[victim->race].race_mod[0], level);
	affect_to_char( ch, victim, &af);

	af.location  = APPLY_DEX;
	af.modifier  = UMIN(race_table[race].race_mod[1] - race_table[victim->race].race_mod[1], level);
	affect_to_char( ch, victim, &af);

	af.location  = APPLY_CON;
	af.modifier  = UMIN(race_table[race].race_mod[2] - race_table[victim->race].race_mod[2], level);
	affect_to_char( ch, victim, &af);

	af.location  = APPLY_RACE;
	af.modifier  = race - victim->race;
	affect_to_char( ch, victim, &af);

	af.location  = APPLY_DISGUISE;
	af.modifier  = level <= 7 ? 10 : level <= 13 ? 15 : 20;
	affect_to_char( ch, victim, &af);

	// equip melds and is nonfunctional unless
	// the form is the same type as the original.
	// inverting wear locs to be restored later - Kregor
	if (race_table[race].body_type != race_table[victim->race].body_type)
	{
		int iWear;

		for (obj = victim->first_carrying ; obj ; obj = obj->next_content)
		{
			if (IS_WORN(obj))
			{
				iWear = obj->wear_loc;
				unequip_char(ch, obj, FALSE);
				obj->wear_loc = 0 - iWear;
			}
		}
	}
	
	char_reset(victim);
	
	if (!IS_NPC(victim))
	{
		victim->pcdata->disguise_roll = dice(1,20);
	}

	if (ch == victim && arcane_mastery(ch, SCHOOL_TRANSMUTATION))
	{
		ch->hit = get_max_hit(ch);
	}

	act( "You slowly start to change into $t $T.", victim, a_an(race_table[get_race(victim)].race_name), race_table[get_race(victim)].race_name, TO_CHAR);
	act( "$n slowly starts to change into $t $T.", victim, a_an(race_table[get_race(victim)].race_name), race_table[get_race(victim)].race_name, TO_ROOM);

	pop_call();
	return TRUE;
}


bool revert_morph( CHAR_DATA *ch, bool fDisplay )
{
	OBJ_DATA *obj;
	AFFECT_DATA *paf, *paf_next;
	int paf_type, old_race;

	push_call("morph(%p,%p)",ch,fDisplay);
	
	if (!ch->apply[APPLY_RACE])
	{
		if (fDisplay)
			act("You are in your original form already.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	old_race = get_race(ch);
	
	for ( paf_type = 0, paf = ch->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;
		
		if (!IS_SET(skill_table[paf->type].spell_desc, SDESC_POLYMORPH) && paf->location != APPLY_RACE)
			continue;
			
		//can't willingly revert from baleful poly - Kregor
		if (fDisplay && skill_table[paf->type].target == TAR_CHAR_OFFENSIVE)
			continue;

		paf_type = paf->type;			
		affect_strip(ch, paf_type);
	}
	
	if (!paf_type)
	{
		if (fDisplay)
			act("You remain in your previous form.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	// equip added after a shapechange falls off unless same race type - Kregor
	if (race_table[old_race].type != race_table[ch->race].type || race_table[old_race].type != RTYPE_HUMANOID)
	{
		for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
		{
			if (IS_WORN(obj))
			{
				unequip_char(ch, obj, FALSE);
			}
			if (obj->wear_loc < WEAR_NONE)
			{
				equip_char(ch, obj, 0 - obj->wear_loc);
			}
		}
	}

	char_reset(ch);
	
	if (!IS_NPC(ch))
	{
		ch->pcdata->disguise_roll = 0;
	}

	if (arcane_mastery(ch, SCHOOL_TRANSMUTATION))
	{
		ch->hit = get_max_hit(ch);
	}

	if (fDisplay)
	{
		act( "You slowly start to change into $t $T.", ch, a_an(race_table[get_race(ch)].race_name), race_table[get_race(ch)].race_name, TO_CHAR);
		act( "$n slowly starts to change into $t $T.", ch, a_an(race_table[get_race(ch)].race_name), race_table[get_race(ch)].race_name, TO_ROOM);
	}
	pop_call();
	return TRUE;
}


void do_revert( CHAR_DATA *ch, char *argument )
{
	push_call("do_revert(%p,%p)",ch,argument);
	
	if (!ch->apply[APPLY_RACE])
	{
		act("You are in your original form already.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return;
	}
	
	revert_morph(ch, TRUE);
	TAKE_ACTION(ch, ACTION_STANDARD);

	pop_call();
	return;
}
	
	
/*
	Bardic Songs
*/

DO_SONG(song_song_of_inspiration)
{
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int hitbonus, skillbonus, acbonus, diceroll, hpbonus;

	push_call("song_song_of_inspiration(%p,%p,%p,%p)",sn,level,ch,vo);
	
	diceroll = perform_roll(ch);
	hitbonus = (level / 5) + 1;
	skillbonus = level / 5;
	acbonus = level / 10;
	hpbonus = level / 10;
	
	for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!is_same_group(ch, vch))
			continue;
		
		af.type      = sn;
		af.duration  = 5 + level/5;
		if (learned(ch, gsn_extend_song))
			af.duration *= 2;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;

		af.location  = APPLY_MOR_TOHIT;
		af.modifier  = hitbonus;
		affect_join( ch, vch, &af );

		af.location  = APPLY_MOR_DAMG;
		af.modifier  = hitbonus;
		affect_join( ch, vch, &af );

		af.location  = APPLY_MOR_WILL;
		af.modifier  = hitbonus;
		affect_join( ch, vch, &af );
		
		if (diceroll >= 5)
		{
			af.location  = APPLY_MOR_REFL;
			af.modifier  = hitbonus;
			affect_join( ch, vch, &af );
		}
		if (diceroll >= 10)
		{
			af.location  = APPLY_MOR_FORT;
			af.modifier  = hitbonus;
			affect_join( ch, vch, &af );
		}
		if (skillbonus && diceroll >= 15)
		{
			af.location  = APPLY_MOR_SKILL;
			af.modifier  = skillbonus;
			affect_join( ch, vch, &af );
		}
		if (acbonus && diceroll >= 20)
		{
			af.location  = APPLY_DODGE;
			af.modifier  = acbonus;
			affect_join( ch, vch, &af );
		}
		if (level > 15 && diceroll >= 25)
		{
			af.location  = APPLY_HIT;
			af.modifier  = dice(hpbonus, class_table[vch->class].hp_max);
			affect_join( ch, vch, &af );
		}	
		act( "{138}You are inspired by the song.", vch, NULL, NULL, TO_CHAR );
	}

	pop_call();
	return TRUE;
}


DO_SONG(song_curse_song)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int hitbonus, skillbonus, acbonus, diceroll;

	push_call("song_curse_song(%p,%p,%p,%p)",sn,level,ch,vo);
	
	hitbonus = (level / 5) + 1;
	skillbonus = level / 5;
	acbonus = level / 10;
	diceroll = perform_roll(ch);

	for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!is_same_group(victim, vch))
			continue;

		if (is_affected(vch, gsn_bardic_song))
			continue;
			
		af.type      = sn;
		af.duration  = 5 + (level / 5);
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF_CURSE;
		af.level     = level;
	
		af.location  = APPLY_MOR_TOHIT;
		af.modifier  = 0-hitbonus;
		affect_to_char( ch, vch, &af );
	
		af.location  = APPLY_MOR_DAMG;
		af.modifier  = 0-hitbonus;
		affect_to_char( ch, vch, &af );
	
		af.location  = APPLY_MOR_WILL;
		af.modifier  = 0-hitbonus;
		affect_to_char( ch, vch, &af );
	
		if (diceroll >= 5)
		{
			af.location  = APPLY_MOR_REFL;
			af.modifier  = 0-hitbonus;
			affect_to_char( ch, vch, &af );
		}
		if (diceroll >= 10)
		{
			af.location  = APPLY_MOR_FORT;
			af.modifier  = 0-hitbonus;
			affect_to_char( ch, vch, &af );
		}
		if (skillbonus && diceroll >= 15)
		{
			af.location  = APPLY_MOR_SKILL;
			af.modifier  = 0-skillbonus;
			affect_to_char( ch, vch, &af );
		}
		if (skillbonus && diceroll >= 20)
		{
			af.location  = APPLY_DODGE;
			af.modifier  = 0-acbonus;
			affect_to_char( ch, vch, &af );
		}
		send_to_char_color( "{108}You feel crushed by sorrow.\n\r", victim );
	}
	
	pop_call();
	return TRUE;
}


DO_SONG(song_song_of_courage)
{
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int diceroll;

	push_call("song_song_of_courage(%p,%p,%p,%p)",sn,level,ch,vo);
	
	diceroll = perform_roll(ch);

	for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!is_same_group(ch, vch))
			continue;
		
		af.type      = sn;
		af.duration  = 5 + level/5;
		if (learned(ch, gsn_extend_song))
			af.duration *= 2;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;

		af.location  = APPLY_SAVE_FEAR;
		af.modifier  = 1;
		affect_join( ch, vch, &af );

		af.location  = APPLY_SAVE_CHARM;
		af.modifier  = diceroll / 5;
		affect_join( ch, vch, &af );

		act( "{138}You are filled with courage.", vch, NULL, NULL, TO_CHAR );
	}
	
	pop_call();
	return TRUE;
}


DO_SONG(song_song_of_heroism)
{
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int diceroll;

	push_call("song_song_of_heroism(%p,%p,%p,%p)",sn,level,ch,vo);
	
	diceroll = perform_roll(ch);

	for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!is_same_group(ch, vch))
			continue;
		
		af.type      = sn;
		af.duration  = 5 + level/5;
		if (learned(ch, gsn_extend_song))
			af.duration *= 2;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;

		af.location  = APPLY_IMM_FEAR;
		af.modifier  = 1;
		affect_join( ch, vch, &af );

		af.location  = APPLY_MOR_SAVES;
		af.modifier  = diceroll / 5;
		affect_join( ch, vch, &af );

		af.location  = APPLY_DODGE;
		af.modifier  = diceroll / 5;
		affect_join( ch, vch, &af );
		
		act( "{138}You are inspired to great heroism.", vch, NULL, NULL, TO_CHAR );
	}
	
	pop_call();
	return TRUE;
}


DO_SONG(song_dirge_of_doom)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int diceroll;

	push_call("song_dirge_of_doom(%p,%p,%p,%p)",sn,level,ch,vo);
	
	diceroll = perform_roll(ch);

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!is_same_group(victim, vch))
			continue;
		if (is_immune(vch, SDESC_MIND))
			continue;
		if (is_immune(vch, SDESC_FEAR))
			continue;
		if (will_save(victim, ch, diceroll, sn))
			continue;
			
		af.type      = sn;
		af.duration  = 5 + level/5;
		if (learned(ch, gsn_extend_song))
			af.duration *= 2;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_FEAR;
		af.level     = level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		affect_join( ch, vch, &af );
		vch->fear_level = UMIN(vch->fear_level + URANGE(1, diceroll/10, 3), 3);

		act( "{118}$n's song fills you with fear!", ch, NULL, vch, TO_CHAR );
	}
	
	pop_call();
	return TRUE;
}


DO_SONG(song_song_of_fascination)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int diceroll;

	push_call("song_song_of_fascination(%p,%p,%p,%p)",sn,level,ch,vo);
	
	diceroll = perform_roll(ch);

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!is_same_group(victim, vch))
			continue;
		if (save_resist(ch, vch, sn, level))
			continue;
			
		af.type      = sn;
		af.duration  = 5 + level/5;
		if (learned(ch, gsn_extend_song))
			af.duration *= 2;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_FASCINATED;
		af.level     = level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		affect_join( ch, vch, &af );

		act( "{178}You gaze at $N in fascination.", vch, NULL, ch, TO_CHAR );
	}
	
	pop_call();
	return TRUE;
}


DO_SONG(song_song_of_freedom)
{
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA *paf, *paf_next;
	int diceroll, cnt, count;
	bool fCursed = FALSE;
	int paf_type;

	push_call("song_song_of_suggestion(%p,%p,%p,%p)",sn,level,ch,vo);
	
	diceroll = perform_roll(ch);
	count = diceroll / 5;

	for (cnt = 0, vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (cnt >= count)
			break;
		if (!is_same_group(ch, vch))
			continue;
		if (vch == ch)
			continue;
			
		fCursed = FALSE;

		for (paf_type = -1, paf = vch->first_affect ; paf ; paf = paf_next)
		{
			paf_next = paf->next;
			
			if (skill_table[paf->type].skilltype == STYPE_CURSE)
			{
				fCursed = TRUE;

				if (diceroll < paf->level)
					continue;

				if (paf->duration < 0)
					continue;

				if (skill_table[paf->type].msg_off)
				{
					act( skill_table[paf->type].msg_off, vch, NULL, NULL, TO_CHAR);
				}
				if (skill_table[paf->type].msg_off_room)
				{
					act( skill_table[paf->type].msg_off_room, vch, NULL, NULL, TO_ROOM);
				}
				paf_type = paf->type;
				affect_from_char(vch, paf);
			}
		}
		if (!fCursed)
			continue;
		cnt++;
		
		if (paf_type == -1)
		{
			act("Nothing seems to happen.", ch, NULL, NULL, TO_CHAR);
		}
	}
	pop_call();
	return TRUE;
}


DO_SONG(song_song_of_soothing)
{
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA *paf, *paf_next;
	AFFECT_DATA af;
	int diceroll;

	push_call("song_song_of_soothing(%p,%p,%p,%p)",sn,level,ch,vo);
	
	diceroll = perform_roll(ch);

	for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!is_same_group(ch, vch))
			continue;
		
		af.type      = sn;
		af.duration  = 5 + level/5;
		if (learned(ch, gsn_extend_song))
			af.duration *= 2;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;

		af.location  = APPLY_FAST_HEALING;
		af.modifier  = diceroll / 5 + 1;
		affect_join( ch, vch, &af );

		if (diceroll >= 20)
		{
			for (paf = vch->first_affect ; paf ; paf = paf_next)
			{
				paf_next = paf->next;
				
				if (IS_SET(paf->bitvector, AFF2_SICKENED) || (diceroll >= 30 && IS_SET(paf->bitvector, AFF2_NAUSEATED)))
				{
					if (paf->duration < 0)
						continue;
	
					if (skill_table[paf->type].msg_off)
					{
						act( skill_table[paf->type].msg_off, vch, NULL, NULL, TO_CHAR);
					}
					if (skill_table[paf->type].msg_off_room)
					{
						act( skill_table[paf->type].msg_off_room, vch, NULL, NULL, TO_ROOM);
					}
					affect_from_char(vch, paf);
				}
			}
		}
		vch->move = UMIN(vch->move + diceroll, get_max_move(vch));
		
		act( "{138}$N's performance soothes your pain and fatigue.", ch, NULL, vch, TO_VICT );
	}
	
	pop_call();
	return TRUE;
}


DO_SONG(song_song_of_sleep)
{
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int diceroll;

	push_call("song_song_of_sleep(%p,%p,%p,%p)",sn,level,ch,vo);
	
	diceroll = perform_roll(ch);

	for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (is_same_group(ch, vch))
			continue;
		if (is_immune(vch, SDESC_SLEEP))
			continue;
		
		af.type      = sn;
		af.duration  = 5 + level/5;
		if (learned(ch, gsn_extend_song))
			af.duration *= 2;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;

		af.location  = APPLY_SIGHT;
		af.modifier  = 0 - (diceroll / 5);
		affect_join( ch, vch, &af );

		af.location  = APPLY_LISTEN;
		af.modifier  = 0 - (diceroll / 5);
		affect_join( ch, vch, &af );

		af.location  = APPLY_SAVING_REFL;
		af.modifier  = 0 - (diceroll / 5);
		affect_join( ch, vch, &af );

		af.location  = APPLY_SAVE_SLEEP;
		af.modifier  = 0 - (diceroll / 5);
		affect_join( ch, vch, &af );

		if (diceroll >= 25)
		{
			if (vch->level > level)
				continue;

			if (will_save(vch, ch, diceroll, sn))
				continue;
	
			af.type      = sn;
			af.duration  = turn * level;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_SLEEP;
			af.level     = level;
			affect_to_char( ch, vch, &af );
	
			if (IS_AWAKE(vch))
			{
				/* Add to make fighting people stop fighting to sleep */
				stop_fighting(vch, FALSE);
				act( "You feel very sleepy ..... zzzzzz.", vch, NULL, NULL, TO_CHAR );
				act( "$n closes $s eyes and drifts to sleep.", vch, NULL, NULL, TO_ROOM );
				update_pos(vch, POS_RESTING);
			}
		}
		if (IS_AWAKE(vch))
		{
			act( "{108}$N's performance makes your eyes feel heavy.", ch, NULL, vch, TO_VICT );
		}
	}
	pop_call();
	return TRUE;
}


DO_SONG(song_song_of_suggestion)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int diceroll, cnt, count;

	push_call("song_song_of_suggestion(%p,%p,%p,%p)",sn,level,ch,vo);
	
	diceroll = perform_roll(ch);
	count = diceroll / 5;

	for (cnt = 0, vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!is_same_group(victim, vch))
			continue;
		if (is_immune(vch, SDESC_MIND))
			continue;
		if (is_immune(vch, SDESC_CHARM))
			continue;
		if (IS_AFFECTED(vch, AFF2_CHARMED))
			continue;
		if (will_save(vch, ch, diceroll, sn))
			continue;
		if (cnt >= count)
			break;
			
		af.type      = sn;
		af.duration  = 5 + level/5;
		if (learned(ch, gsn_extend_song))
			af.duration *= 2;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		affect_join( ch, vch, &af );

		act( "{138}You hang on $n's every word.", ch, NULL, vch, TO_CHAR );
		cnt++;
	}
	
	pop_call();
	return TRUE;
}


/*
	Breath Spells
	Do not need ForReal flags, because they're instant
*/

DO_SPELL(spell_acid_spittle)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int nodice;

	act( "You spit a stream of acidic saliva.", ch, NULL, NULL, TO_CHAR);
	act( "$n spits a stream of acidic saliva.", ch, NULL, NULL, TO_ROOM);
	
	push_call("spell_acid_spittle(%p,%p,%p,%p)",sn,level,ch,vo);
	
	nodice = ch->level + 1;

	spell_damage(ch, victim, dice(nodice, 4), sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_acid_breath)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	int dam, nodice;

	push_call("spell_acid_breath(%p,%p,%p,%p)",sn,level,ch,vo);

	nodice = ch->level * 2 / 3;

	act( "You breathe a cloud of caustic vapor.", ch, NULL, NULL, TO_CHAR);
	act( "$n breathes a cloud of caustic vapor.", ch, NULL, NULL, TO_ROOM);
	
	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		dam = dice(nodice, 4);
		
		if (!can_mass_cast(ch, vch, sn))
			continue;
			
		if (IS_NPC(ch) && !is_same_group(victim, vch))
			continue;
		
		if (refl_save(vch, ch, ch->level, sn))
		{
			if (can_evade(vch))
			{
				send_to_char_color( "{138}You quickly evade the acid blast.\n\r", vch );
				dam = 0;
			}
			else
			{
				dam /= 2;
			}				
		}
		else
		{
			if (learned(vch, gsn_imp_evasion) && can_evade(vch))
			{
				send_to_char_color( "{138}You evade the brunt of the acid blast.\n\r", vch );
				dam /= 2;
			}
		}
		damage( ch, vch, dam, sn, NULL );
		
		if (dam > 0)
		{
			AFFECT_DATA af;
			
			af.type      = sn;
			af.duration  = 1 + (level/3);
			af.location  = 0;
			af.modifier  = 0;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			af.level     = level;
			affect_join( ch, vch, &af );
			
			act( "{128}Caustic acid begins to eat away at $n.", vch, NULL, NULL, TO_ROOM);
			act( "{128}Caustic acid begins to eat away at you!", vch, NULL, NULL, TO_CHAR);
		}
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_fire_breath)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int nodice;
	
	push_call("spell_fire_breath(%p,%p,%p,%p)",sn,level,ch,vo);

	act( "A blast of fire spews from your maw.", ch, NULL, NULL, TO_CHAR);
	act( "A blast of fire spews from $n's maw.", ch, NULL, NULL, TO_ROOM);
	
	nodice = ch->level / 2;

	spell_damage(ch, victim, dice(nodice, 10), sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_frost_breath)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int nodice;
	
	push_call("spell_frost_breath(%p,%p,%p,%p)",sn,level,ch,vo);

	act( "A cone of cold blasts from your maw.", ch, NULL, NULL, TO_CHAR);
	act( "A cone of cold blasts from $n's maw.", ch, NULL, NULL, TO_ROOM);
	
	nodice = ch->level / 3;

	spell_damage(ch, victim, dice(nodice, 6), sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_gas_breath)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	int dam, nodice;

	push_call("spell_gas_breath(%p,%p,%p,%p)",sn,level,ch,vo);

	nodice = ch->level / 2;

	act( "You breathe a cloud of noxious fumes.", ch, NULL, NULL, TO_CHAR);
	act( "$n breathes a cloud of noxious fumes.", ch, NULL, NULL, TO_ROOM);

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		dam = dice(nodice, 6);
		
		if (!can_mass_cast(ch, vch, sn))
			continue;
			
		if (IS_NPC(ch) && !is_same_group(victim, vch))
			continue;
		
		if (refl_save(vch, ch, ch->level, sn))
		{
			if (can_evade(vch))
			{
				send_to_char_color( "{138}You quickly evade the cloud of gas.\n\r", vch );
				dam = 0;
			}
			else
			{
				dam /= 2;
			}				
		}
		else
		{
			if (learned(vch, gsn_imp_evasion) && can_evade(vch))
			{
				send_to_char_color( "{138}You evade the brunt of the gas.\n\r", vch );
				dam /= 2;
			}
			else
			{
				if (!fort_save(victim, ch, ch->level, sn))
				{
					AFFECT_DATA af;
		
					af.type      = gsn_poison;
					af.location  = APPLY_NONE;
					af.modifier  = 0;
					af.duration  = nodice;
					af.bittype   = AFFECT_TO_CHAR;
					af.bitvector = AFF_POISON;
					af.level     = level;
					affect_join( ch, vch, &af );
		
					send_to_char( "You choke and gag in the cloud of poison gas.\n\r", vch );
					act( "$n chokes and gags in the cloud of poison gas.\n\r", vch, NULL, NULL, TO_ROOM );
				}
			}
		}
		damage( ch, vch, dam, sn, NULL );
	}		

	pop_call();
	return TRUE;
}

DO_SPELL(spell_lightning_breath)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int nodice;
	
	push_call("spell_lightning_breath(%p,%p,%p,%p)",sn,level,ch,vo);

	act( "Bolts of lightning streak from your maw.", ch, NULL, NULL, TO_CHAR);
	act( "Bolts of lightning streak from $n's maw.", ch, NULL, NULL, TO_ROOM);

	nodice = ch->level / 3;

	spell_damage(ch, victim, dice(nodice, 8), sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_psionic_shockwave)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_psionic_shockwave(%p,%p,%p,%p)",sn,level,ch,vo);

	dam = dice(level, 10);

	spell_damage(ch, victim, dam, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_sonic_blast)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int nodice;

	push_call("spell_sonic_blast(%p,%p,%p,%p)",sn,level,ch,vo);

	nodice = ch->level / 3;

	act( "A shrill blast emits from you.", ch, NULL, NULL, TO_CHAR);
	act( "A shrill blast emits from $n.", ch, NULL, NULL, TO_ROOM);

	spell_damage(ch, victim, dice(nodice, 8), sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_weakness_gas)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int nodice;

	push_call("spell_weakness_gas(%p,%p,%p,%p)",sn,level,ch,vo);
	
	nodice = ch->level / 4;

	act( "A cloud of gas plumes from your nostrils.", ch, NULL, NULL, TO_CHAR);
	act( "A cloud of gas plumes from $n's nostrils.", ch, NULL, NULL, TO_ROOM);
	
	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn))
			continue;
			
		if (IS_NPC(ch) && !is_same_group(victim, vch))
			continue;
		
		if (save_resist(ch, vch, sn, level))
			continue;

		af.type      = sn;
		af.location  = APPLY_STR;
		af.modifier  = 0-nodice;
		af.duration  = dice(1, level/4);
		af.bittype   = AFF_NONE;
		af.bitvector = AFF_NONE;
		af.level     = level;
		affect_to_char( ch, vch, &af );

		send_to_char_color( "{118}You feel the gas sapping your strength.\n\r", vch );
		act( "$n {118}'s strength saps away in the gas.", vch, NULL, NULL, TO_ROOM);
	}

	pop_call();
	return TRUE;
}


DO_SPELL(spell_paralysis_gas)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;

	push_call("spell_paralysis_gas(%p,%p,%p,%p)",sn,level,ch,vo);
	
	act( "A cloud of gas plumes from your nostrils.", ch, NULL, NULL, TO_CHAR);
	act( "A cloud of gas plumes from $n's nostrils.", ch, NULL, NULL, TO_ROOM);
	
	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn))
			continue;
			
		if (IS_NPC(ch) && !is_same_group(victim, vch))
			continue;
		
		if (save_resist(ch, vch, sn, level))
			continue;			

		af.type      = sn;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.duration  = dice(1, level/4);
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_PARALYSIS;
		af.level     = level;
		affect_to_char( ch, vch, &af );

		send_to_char_color( "{118}The cloud of gas leaves you helpless!\n\r", vch );
		act( "$n {118}is helpless in a cloud of gas!", vch, NULL, NULL, TO_ROOM);
	}		

	pop_call();
	return TRUE;
}


DO_SPELL(spell_fear_gas)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;

	push_call("spell_fear_gas(%p,%p,%p,%p)",sn,level,ch,vo);
	
	act( "A cloud of gas plumes from your nostrils.", ch, NULL, NULL, TO_CHAR);
	act( "A cloud of gas plumes from $n's nostrils.", ch, NULL, NULL, TO_ROOM);
	
	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn))
			continue;
			
		if (IS_NPC(ch) && !is_same_group(victim, vch))
			continue;
		
		if (save_resist(ch, vch, sn, level))
			continue;			

		af.type      = sn;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.duration  = dice(1, level/4);
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_FEAR;
		af.level     = level;
		affect_to_char( ch, vch, &af );
		vch->fear_level += UMIN(vch->fear_level + 2, 3);

		if (IS_NPC(vch))
		{
			if (!IS_SET(vch->act, ACT_WIMPY))
				SET_BIT(vch->act, ACT_WIMPY);
		}

		send_to_char_color( "{118}You try to run away from your attacker!\n\r", vch );
		act( "$n {118}tries to run away from $s attacker!", vch, NULL, NULL, TO_ROOM);
		do_withdraw(ch, NULL);
	}		

	pop_call();
	return TRUE;
}


DO_SPELL(spell_slow_gas)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;

	push_call("spell_slow_gas(%p,%p,%p,%p)",sn,level,ch,vo);
	
	act( "A cloud of gas plumes from your nostrils.", ch, NULL, NULL, TO_CHAR);
	act( "A cloud of gas plumes from $n's nostrils.", ch, NULL, NULL, TO_ROOM);
	
	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn))
			continue;
			
		if (IS_NPC(ch) && !is_same_group(victim, vch))
			continue;
		
		if (save_resist(ch, vch, sn, level))
			continue;
			
		if (is_affected(vch, gsn_haste))
		{
			affect_strip(vch, gsn_haste);
			continue;
		}		

		af.type      = gsn_slow;
		af.duration  = level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_STAGGERED;
		af.level     = level;
		affect_join( ch, vch, &af);
	
		af.location  = APPLY_DODGE;
		af.modifier = -1;
		affect_join( ch, vch, &af);
	
		af.location  = APPLY_SAVING_REFL;
		af.modifier = -1;
		affect_join( ch, vch, &af);
	
		af.location  = APPLY_COMP_TOHIT;
		af.modifier = -1;
		affect_join( ch, vch, &af);
	
		send_to_char_color( "{118}The cloud of gas slows you down!\n\r", vch );
		act( "$n {118}is slowed in the cloud of gas!", vch, NULL, NULL, TO_ROOM);
	}		

	pop_call();
	return TRUE;
}


DO_SPELL(spell_euphoria_gas)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;

	push_call("spell_euphoria_gas(%p,%p,%p,%p)",sn,level,ch,vo);
	
	act( "{158}A cloud of gas plumes from your nostrils.", ch, NULL, NULL, TO_CHAR);
	act( "{158}A cloud of gas plumes from $n's nostrils.", ch, NULL, NULL, TO_ROOM);
	
	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn))
			continue;
			
		if (save_resist(ch, vch, sn, level))
			continue;

		af.type      = sn;
		af.duration  = level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_STAGGERED;
		af.level     = level;
		affect_join( ch, vch, &af);
		
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_SICKENED;
		affect_join( ch, vch, &af);
		
		af.location  = APPLY_IMM_FEAR;
		af.modifier = 1;
		affect_join( ch, vch, &af);
	
		send_to_char_color( "{158}OOoooh! Pretty colors....\n\r", vch );
		act( "$n {158}suddenly seems light headed and giddy.", vch, NULL, NULL, TO_ROOM);
	}		

	pop_call();
	return TRUE;
}


/*
 * Spell functions for current spells
 */
 
/*
 * All standard affecting spells should go thru this function
 * uses target mode of spell to determine whether save or resistance applies.
 */
DO_SPELL(spell_affect)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int cnt, save;
	bool area = FALSE;

	push_call("spell_affect(%p,%p,%p,%p)",sn,level,ch,vo);

	if (is_affected(victim, gsn_time_stop))
	{
		act("You cannot affect $n.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (IS_AREA_SPELL(sn))
		area = TRUE;
		
	for (cnt = 0, vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (is_affected(vch, gsn_time_stop))
		{
			act("You cannot affect $n.", ch, NULL, vch, TO_CHAR);
			break;
		}
	
		if (!area && vch != victim)
			continue;
			
		if (cnt && cnt >= level)
			break;
			
		if (IS_OFFENSIVE(vch, sn))
		{
			if (area && IS_NPC(ch) && !is_same_group(victim, vch))
				continue;
		}
		else if (sn != gsn_prayer)
		{
			if (area && !is_same_group(victim, vch))
				continue;
		}
			
		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
			continue;

		af.type     	= sn;
		af.level    	= level;

		if (sn == gsn_foresight)
		{
			af.duration		= turn * 2 * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_INS_AC;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_INS_REFL;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);
			
			act( "{138}You feel keenly aware to the dangers around you.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n grows keenly aware of $s surroundings.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_good_hope)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_MOR_TOHIT;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_DAMG;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_SAVES;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_SKILL;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);
			
			act( "{138}You feel a rush of confidence and hope.", vch, NULL, NULL, TO_CHAR);
		}
		if (sn == gsn_crushing_despair)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_MOR_TOHIT;
			af.modifier 	= -2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_DAMG;
			af.modifier 	= -2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_SAVES;
			af.modifier 	= -2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_SKILL;
			af.modifier 	= -2;
			affect_join( ch, vch, &af);
			
			act( "{108}You feel weighted with despair.", vch, NULL, NULL, TO_CHAR);
		}
		if (sn == gsn_prayer)
		{
			if (is_same_group(vch, victim))
			{
				af.duration		= level;
				af.bittype  	= AFFECT_TO_NONE;
				af.bitvector	= AFF_NONE;
				af.location 	= APPLY_LUCK_TOHIT;
				af.modifier 	= 1;
				affect_join( ch, vch, &af);
	
				af.location 	= APPLY_LUCK_SAVES;
				af.modifier 	= 1;
				affect_join( ch, vch, &af);
	
				af.location 	= APPLY_LUCK_DAMG;
				af.modifier 	= 1;
				affect_join( ch, vch, &af);
	
				af.location 	= APPLY_LUCK_SKILL;
				af.modifier 	= 1;
				affect_join( ch, vch, &af);
				
				act( "{138}Divine favor fills you.", vch, NULL, NULL, TO_CHAR);
				act( "{138}Divine favor fills $n.", vch, NULL, NULL, TO_ROOM);
			}
			else
			{
				af.duration		= level;
				af.bittype  	= AFFECT_TO_NONE;
				af.bitvector	= AFF_NONE;
				af.location 	= APPLY_LUCK_TOHIT;
				af.modifier 	= -1;
				affect_join( ch, vch, &af);
	
				af.location 	= APPLY_LUCK_SAVES;
				af.modifier 	= -1;
				affect_join( ch, vch, &af);
	
				af.location 	= APPLY_LUCK_DAMG;
				af.modifier 	= -1;
				affect_join( ch, vch, &af);
	
				af.location 	= APPLY_LUCK_SKILL;
				af.modifier 	= -1;
				affect_join( ch, vch, &af);
				
				act( "{108}You feel divine wrath upon you!", vch, NULL, NULL, TO_CHAR);
			}
		}
		if (sn == gsn_aid)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_MOR_TOHIT;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_WILL;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_HIT;
			af.modifier 	= spell_dice(ch, sn, 1, 8) + UMIN(level, 10);
			affect_join( ch, vch, &af);
			
			act( "{138}Your spirits feel lifted.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n's spirits seem lifted.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_bless)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
	
			af.location  = APPLY_MOR_TOHIT;
			af.modifier  = 1;
			affect_join( ch, vch, &af );
	
			af.location  = APPLY_SAVE_FEAR;
			af.modifier  = 1;
			affect_join( ch, vch, &af );
	
			act( "{138}A divine blessing is laid upon $n.", vch, NULL, NULL, TO_ROOM );
			act( "{138}A divine blessing is laid upon you.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_bane)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
	
			af.location  = APPLY_MOR_TOHIT;
			af.modifier  = -1;
			affect_join( ch, vch, &af );
	
			af.location  = APPLY_SAVE_FEAR;
			af.modifier  = -1;
			affect_join( ch, vch, &af );
	
			act( "{138}A divine banality is laid upon $n.", vch, NULL, NULL, TO_ROOM );
			act( "{138}A divine banality is laid upon you.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_symbol_of_pain)
		{
			if (!cnt)
			{
				act("{138}A bright, glowing rune radiates around $n.", ch, NULL, NULL, TO_ROOM);
				act("{138}A bright, glowing rune radiates around you.", ch, NULL, NULL, TO_ROOM);
			}
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
	
			af.location  = APPLY_MOR_TOHIT;
			af.modifier  = -4;
			affect_join( ch, vch, &af );
	
			af.location  = APPLY_MOR_SKILL;
			af.modifier  = -4;
			affect_join( ch, vch, &af );
	
			act( "{118}$n cringes in pain!.", vch, NULL, NULL, TO_ROOM );
			act( "{118}You are wracked with pain as you glimpse the symbol!", vch, NULL, NULL, TO_CHAR );
			cnt++;
		}
		if (sn == gsn_blindness)
		{
			af.duration  = -1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_BLIND;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{108}You blink, and cannot see a thing!", vch, NULL, NULL, TO_CHAR );
			act( "{108}$n blinks, and cannot see!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_silence)
		{
			af.duration  = level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_SILENCE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{108}You open your mouth, and nothing comes out!", vch, NULL, NULL, TO_CHAR );
			act( "{108}$n can't make a sound.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_web)
		{
			af.duration  = turn*level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_ENTANGLED;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{108}You are covered in sticky webs!", vch, NULL, NULL, TO_CHAR );
			act( "{108}$n is covered in sticky webs!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_daze)
		{
			af.duration  = 2;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_DAZED;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{300}You think to yourself, {200}'What was I doing again?'", vch, NULL, NULL, TO_CHAR );
			act( "{178}$n stands in a daze.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_festering_wounds)
		{
			if (vch->hit >= get_max_hit(vch))
			{
				act("There are no wounds on $N to fester", ch, NULL, vch, TO_CHAR);
				continue;
			}
			af.duration  = save == PARTIAL ? 1 : -1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_CURSE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{118}Your skin erupts into festers!", vch, NULL, NULL, TO_CHAR );
			act( "{118}$n's skin erupts into festers!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_bleeding_wounds)
		{
			if (vch->hit >= get_max_hit(vch))
			{
				act("There are no wounds on $N to bleed", ch, NULL, vch, TO_CHAR);
				continue;
			}
			af.duration  = -1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_BLEEDING;
			af.location  = APPLY_NONE;
			af.modifier  = UMAX(1, ROUNDUP(4 - vch->hit * 4 / get_max_hit(vch)));
			affect_join( ch, vch, &af );
	
			act( "{118}Your wounds bleed copiously!", vch, NULL, NULL, TO_CHAR );
			act( "{118}$n's wounds bleed copiously!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_drown)
		{
			if (!must_breathe(vch))
			{
				act("$N does not have to breathe.", ch, NULL, vch, TO_CHAR);
				continue;
			}
			af.duration  = -1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_DROWNING;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{168}Your lungs fill with water!", vch, NULL, NULL, TO_CHAR );
			act( "{118}$n starts to gasp for air!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_irresistible_dance)
		{
			if (!IS_AWAKE(vch))
			{
				act("$N is in no condition for dancing.", ch, NULL, vch, TO_CHAR);
				continue;
			}
			
			if (is_mounting(vch))
				do_dismount(vch, "");
			else
				do_stand(vch, "");

			af.duration  = save == PARTIAL ? 1 : dice(1,4) + 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
	
			af.location  = APPLY_SAVING_REFL;
			af.modifier  = -10;
			affect_join( ch, vch, &af );
	
			af.location  = APPLY_DODGE;
			af.modifier  = -4;
			affect_join( ch, vch, &af );
	
			act( "{138}$n begins to shuffle $s feet.", vch, NULL, NULL, TO_ROOM );
			act( "{138}You start to dance uncontrollably.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_comprehend_languages)
		{
			af.duration  = hr * level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_UNDERSTAND;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{178}You start understanding everyone.", vch, NULL, NULL, TO_CHAR);
			act( "{178}$n's face brightens with understanding.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_darkvision)
		{
			af.duration  = hr * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_DARKVISION;
			af.modifier  = 60;
			affect_join( ch, vch, &af );
	
			act( "{108}Your eyes adjust to see clearly in the darkness.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_disguise_self)
		{
			af.duration  = hr * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_DISGUISE;
			af.modifier  = 10;
			affect_join( ch, vch, &af );
	
			act( "{108}Your gain an illusory enhancement to your disguise.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_delay_poison)
		{
			af.duration  = hr * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_IMM_POISON;
			af.modifier  = 1;
			affect_join( ch, vch, &af );
	
			act( "{128}You feel a ward against poison fill your body.", vch, NULL, NULL, TO_CHAR );
			if ( ch != vch )
				act( "{128}$N is filled with a ward against poisons.", ch, NULL, vch, TO_CHAR );
		}
		if (sn == gsn_detect_invis)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_DETECT_INVIS;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{178}Your eyes fixate as they gain the ability to see the unseen.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_detect_magic)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{148}Traces of blue radiate from things magical.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_detect_secret)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_DETECT_HIDDEN;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{148}Traces of blue radiate from things magical.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_detect_traps)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_DETECT_TRAPS;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{178}Your senses are heightened to detect traps.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_detect_thoughts)
		{
			af.duration  = level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{068}Random thoughts in your vicinity trickle into your mind.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_bestow_grace)
		{
			if (IS_GOOD(vch))
			{
				af.duration  = turn * level;
				af.bittype   = AFFECT_TO_NONE;
				af.bitvector = AFF_NONE;
				af.location  = APPLY_NONE;
				af.modifier  = 0;
				affect_join( ch, vch, &af );
		
				act( "{138}You radiate with divine grace!", vch, NULL, NULL, TO_CHAR );
				act( "{138}$n radiates with divine grace!", vch, NULL, NULL, TO_ROOM );
			}
		}
		if (sn == gsn_repel_metal)
		{
			af.duration  = level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "Metal begins to repel away from you!", vch, NULL, NULL, TO_CHAR );
			act( "Metal begins to repel away from $n!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_entropic_shield)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{158}A c{118}ha{038}ot{138}ic {128}fi{168}el{148}d o{058}f m{158}ul{118}ti{038}co{138}lo{128}re{168}d h{148}ue{058}s {158}surrounds $n.", vch, NULL, NULL, TO_ROOM );
			act( "{158}A c{118}ha{038}ot{138}ic {128}fi{168}el{148}d o{058}f m{158}ul{118}ti{038}co{138}lo{128}re{168}d h{148}ue{058}s {158}surrounds you.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_flesh_to_stone)
		{
			af.duration  = -1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_PETRIFICATION;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{108}You quickly freeze in your tracks!", vch, NULL, NULL, TO_CHAR );
			act( "{108}$n turns into a stone statue!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_astral_projection)
		{
			af.duration  = hr * (domain_apotheosis(ch, DOMAIN_TRAVEL) ? level * 2 : level);
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_ASTRAL;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "$n steps outside of $s body.", vch, NULL, NULL, TO_ROOM );
			act( "You step outside your body.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_ethereal)
		{
			af.duration  = turn * (domain_apotheosis(ch, DOMAIN_TRAVEL) ? level * 2 : level);
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_ETHEREAL;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "$n partially fades out.", vch, NULL, NULL, TO_ROOM );
			act( "You become ethereal.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_empty_body)
		{
			af.duration  = level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_ETHEREAL;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "$n partially fades out.", vch, NULL, NULL, TO_ROOM );
			act( "You become ethereal.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_hallucinate)
		{
			if (IS_AFFECTED(vch, AFF_TRUESIGHT))	
				continue;

			af.duration  = level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_HALLUCINATE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			af.location  = APPLY_SAVING_REFL;
			af.modifier = -2;
			affect_join( ch, vch, &af);

			af.location  = APPLY_SIGHT;
			af.modifier = -2;
			affect_join( ch, vch, &af);
	
			act( "Things don't appear to be the way they were...", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_mind_blank)
		{
			af.duration  = hr * level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_MIND_BLANK;
			af.location  = APPLY_SAVE_MIND;
			af.modifier  = 8;
			affect_join( ch, vch, &af );
	
			act( "{138}Your mind withdraws from detection.", vch, NULL, NULL, TO_CHAR );
			if ( ch != vch )
				act( "{138}$N's mind withdraws from detection.", ch, NULL, vch, TO_CHAR );
		}
		if (sn == gsn_mirror_image)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_NONE;
			af.modifier  = UMIN(spell_dice(ch, sn, 1,4) + (level/3), 8);
			affect_join( ch, vch, &af );
	
			act( "{168}Several images of you step out and away from you.", vch, NULL, NULL, TO_CHAR );
			act ("{168}Several images of $n step out and away from $m.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_haste)
		{
			if (is_affected(vch, gsn_slow))
			{
				affect_strip(vch, gsn_slow);
				continue;
			}
			af.duration  = level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_HASTE;
			af.location  = APPLY_DODGE;
			af.modifier  = 1;
			affect_join( ch, vch, &af );
	
			af.location  = APPLY_SAVING_REFL;
			af.modifier = 1;
			affect_join( ch, vch, &af);
		
			af.location  = APPLY_COMP_TOHIT;
			af.modifier = 1;
			affect_join( ch, vch, &af);

			act( "{138}You speed up.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n speeds up.", vch, NULL, NULL, TO_ROOM);
			cnt++;
		}
		if (sn == gsn_freedom_of_movement)
		{
			af.duration  = 5 * turn * level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_FREEDOM;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{138}You are free from any restraints.", vch, NULL, NULL, TO_CHAR );
			act( "{138}$N moves about freely.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_vigilance)
		{
			af.duration		= hr;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_LISTEN;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_SIGHT;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_SENSE_MOT;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_SAVE_SLEEP;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);
			
			act( "{138}Your alertness sharpens.", vch, NULL, NULL, TO_CHAR);
			cnt++;
		}
		if (sn == gsn_nondetection)
		{
			af.duration  = hr * level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_NONDETECTION;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{138}You are warded against divination.", vch, NULL, NULL, TO_CHAR );
			if ( ch != vch )
				act( "{138}$N is warded against divination.", ch, NULL, vch, TO_CHAR );
		}
		if (sn == gsn_pass_door)
		{
			af.duration  = hr * level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_GASEOUS;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );

			af.location  = APPLY_DR_MAGIC;
			af.modifier  = 10;
			affect_join( ch, vch, &af );

			af.location  = APPLY_FORTIFICATION;
			af.modifier  = 100;
			affect_join( ch, vch, &af );
	
			act( "{108}$n's form turns translucent.", vch, NULL, NULL, TO_ROOM );
			act( "{108}$Your form turns translucent.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_pass_without_trace)
		{
			af.duration  = hr * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{138}$n suddenly leaves no trail of $s passing.", vch, NULL, NULL, TO_ROOM );
			act( "{138}You suddenly leave to trail of your passing.", vch, NULL, NULL, TO_CHAR );
			cnt++;
		}
		if (sn == gsn_read_magic)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			send_to_char_color( "{138}Your mind now comprehends arcane writings.\n\r", vch );
		}
		if (sn == gsn_guidance)
		{
			af.duration  = turn;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			af.location  = APPLY_COMP_SKILL;
			af.modifier  = 1;
			affect_join( ch, vch, &af );
	
			act( "{138}You are touched with divine guidance.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n is touched with divine guidance.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_sanctuary)
		{
			af.duration  = level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_SANCTUARY;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{178}$n is surrounded by a white aura.", vch, NULL, NULL, TO_ROOM );
			act( "{178}You are surrounded by a white aura.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_spell_immunity)
		{
			af.duration  = level;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_IMMUNE_SPELL;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "{138}A powerful antimagic ward surrounds $n", vch, NULL, NULL, TO_ROOM );
			act( "{138}A powerful antimagic aura surrounds you!", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_shield)
		{
			af.duration  = level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_SHIELD;
			af.modifier  = 4;
			affect_join( ch, vch, &af );
	
			act( "{178}A hovering shield of force forms in front of $n.", vch, NULL, NULL, TO_ROOM );
			act( "{178}A hovering shield of force forms in front of you.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_spell_turning)
		{
			af.duration  = 5 * turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_NONE;
			af.modifier  = spell_dice(ch,sn,1,4) + 6;
			affect_join( ch, vch, &af );
	
			act( "A shield of reflective magic coalesces around $n.", vch, NULL, NULL, TO_ROOM );
			act( "A shield of reflective magic coalesces around you.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_status)
		{
			af.duration  = hr * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			affect_join( ch, vch, &af );
	
			act( "You attune your mind to your comrades.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_stone_skin)
		{
			af.duration  = 2 * turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_DR_ADAMANTINE;
			af.modifier  = 10;
			affect_join( ch, vch, &af );
	
			vch->absorption[af.location] = UMIN(10 * level, 150);

			act( "{108}$n's skin turns to malleable stone.", vch, NULL, NULL, TO_ROOM );
			act( "{108}Your skin turns to malleable stone.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_iron_body)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_DR_ADAMANTINE;
			af.modifier  = 15;
			affect_join( ch, vch, &af );

			af.location  = APPLY_IMM_DISEASE;
			af.modifier  = 1;
			affect_join( ch, vch, &af );
	
			af.location  = APPLY_IMM_POISON;
			af.modifier  = 1;
			affect_join( ch, vch, &af );
	
			af.location  = APPLY_FORTIFICATION;
			af.modifier  = 100;
			affect_join( ch, vch, &af );
	
			af.location  = APPLY_STR;
			af.modifier  = 6;
			affect_join( ch, vch, &af );
	
			act( "{108}$n's body turns into living iron!", vch, NULL, NULL, TO_ROOM );
			act( "{108}Your body turns into living iron!", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_identify)
		{
			af.duration  = 3*level;
			af.location  = 0;
			af.modifier  = 0;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_to_char( ch, vch, &af );
		
			send_to_char_color( "{148}Your perception of magical properties grows keen.\n\r", vch );
		}
		if (sn == gsn_clairvoyance)
		{
			af.duration  = level;
			af.location  = 0;
			af.modifier  = 0;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_to_char( ch, vch, &af );
		
			send_to_char_color( "{138}Your eyes focus upon a distance.\n\r", vch );
		}
		if (sn == gsn_ray_of_enfeeblement)
		{
			af.duration  = level * turn;
			af.location  = APPLY_STR;
			af.modifier  = 0 - (spell_dice(ch, sn, 1,6) + UMIN(level/2, 5));
			if (save == -1)
				af.modifier /= 2;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af );

			act( "Your strength flows out of your body.", vch, NULL, NULL, TO_CHAR );
			act( "$n suddenly seems much weaker.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_touch_of_idiocy)
		{
			af.duration = turn * level;
			af.bittype = AFFECT_TO_NONE;
			af.bitvector = 0;
		
			af.location = APPLY_INT;
			af.modifier = 0 - spell_dice(ch, sn, 1, 6);
			affect_join( ch, vch, &af);
		
			af.location = APPLY_WIS;
			af.modifier = 0 - spell_dice(ch, sn, 1, 6);
			affect_join( ch, vch, &af);
		
			af.location = APPLY_CHA;
			af.modifier = 0 - spell_dice(ch, sn, 1, 6);
			affect_join( ch, vch, &af);
		
			act( "$s seems to grow a bit duller.", vch, NULL, NULL, TO_ROOM );
			act( "Your mind no longer feels very sharp.", vch, NULL, NULL, TO_CHAR);
		}
		if (sn == gsn_feeblemind)
		{
			af.duration = -1;
			af.bittype = AFFECT_TO_NONE;
			af.bitvector = 0;
		
			af.location = APPLY_INT;
			af.modifier = -1 * (vch->perm_int - 1);
			affect_join( ch, vch, &af);
		
			af.location = APPLY_CHA;
			af.modifier = -1 * (vch->perm_cha - 1);
			affect_join( ch, vch, &af);
		
			act( "$s begins to babble incoherently.", vch, NULL, NULL, TO_ROOM );
			act( "Your mind clouds and you babble incoherently.", vch, NULL, NULL, TO_CHAR);
		}
		if (sn == gsn_curse)
		{
			af.duration  = -1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_CURSE;
		
			af.location  = APPLY_LUCK_SAVES;
			af.modifier  = -4;
			affect_to_char( ch, vch, &af );
		
			af.location  = APPLY_LUCK_TOHIT;
			af.modifier  = -4;
			affect_to_char( ch, vch, &af );
		
			af.location  = APPLY_LUCK_SKILL;
			af.modifier  = -4;
			affect_to_char( ch, vch, &af );
	
			act( "{108}A powerful curse befalls $n.", vch, NULL, NULL, TO_ROOM);
			act( "{108}A powerful curse befalls you.", vch, NULL, NULL, TO_CHAR);
		}
		if (sn == gsn_bears_endurance || sn == gsn_mass_bears_endurance)
		{
			af.location  = APPLY_CON;
			af.duration = level * 10;
			af.modifier = spell_dice(ch, sn, 1, 4) + 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af);
			cnt++;

			act("{138}You feel the endurance of a bear.", vch, NULL, NULL, TO_CHAR );
			act( "$n {138}flexes with renewed endurance.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_bulls_strength || sn == gsn_mass_bulls_strength)
		{
			af.location  = APPLY_STR;
			af.duration = level * 10;
			af.modifier = spell_dice(ch, sn, 1, 4) + 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af);
			cnt++;

			send_to_char_color( "{138}You feel the strength of a bull ripple through your muscles.\n\r", vch );
			act( "$n {138}flexes with renewed strength.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_animal_growth)
		{
			if (!IS_ANIMAL(vch))
				continue;
			af.location  = APPLY_SIZE;
			af.duration  = level * turn;
			af.modifier  = 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_join( ch, vch, &af);

			af.location  = APPLY_NATURAL_AC;
			af.modifier  = 2;
			affect_join( ch, vch, &af);
			cnt++;

			send_to_char_color( "{138}You quickly grow to twice your size!\n\r", vch );
			act( "{138}$n quickly grows to twice $s size!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_reduce_animal)
		{
			if (!IS_ANIMAL(vch))
				continue;
			if (!is_same_group(ch, vch))
			{
				act("$N is not a willing recipient.", ch, NULL, vch, TO_CHAR);
				continue;
			}
			af.location  = APPLY_SIZE;
			af.duration  = level * hr;
			af.modifier  = 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_join( ch, vch, &af);

			send_to_char_color( "{138}You rapidly shrink to half your size!\n\r", vch );
			act( "{138}$n rapidly shrinks to half $s size!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_enlarge_person || sn == gsn_mass_enlarge_person)
		{
			if (!IS_HUMANOID(vch))
				continue;
			af.location  = APPLY_SIZE;
			af.duration  = level * turn;
			af.modifier  = 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_join( ch, vch, &af);
			cnt++;

			send_to_char_color( "{138}You quickly grow to twice your size!\n\r", vch );
			act( "{138}$n quickly grows to twice $s size!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_reduce_person || sn == gsn_mass_reduce_person)
		{
			if (!IS_HUMANOID(vch))
				continue;
			af.location  = APPLY_SIZE;
			af.duration  = level * turn;
			af.modifier  = 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_join( ch, vch, &af);
			cnt++;

			send_to_char_color( "{138}You rapidly shrink to half your size!\n\r", vch );
			act( "{138}$n rapidly shrinks to half $s size!", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_cats_grace || sn == gsn_mass_cats_grace)
		{
			af.location  = APPLY_DEX;
			af.duration = level * 10;
			af.modifier = spell_dice(ch, sn, 1, 4) + 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af);
			cnt++;

			send_to_char_color( "{138}You feel agile and flexible as the a cat's grace fills you.\n\r", vch);
			act( "$n {138}looks more agile.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_eagles_splendour || sn == gsn_mass_eagles_splendour)
		{
			af.location  = APPLY_CHA;
			af.duration = level * 10;
			af.modifier = spell_dice(ch, sn, 1, 4) + 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af);
			cnt++;

			send_to_char_color( "{138}You beam with raidance as an eagle's spendour surrounds you.\n\r", vch);
			act( "$n {138}beams with radiance and charisma.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_foxs_cunning || sn == gsn_mass_foxs_cunning)
		{
			af.location  = APPLY_INT;
			af.duration = level * 10;
			af.modifier = spell_dice(ch, sn, 1, 4) + 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af);
			cnt++;

			send_to_char_color( "{138}Your eyes shine brightly as a fox's cunning sharpens your mind.\n\r", vch);
			act( "$n's {138}eyes shine brightly.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_owls_wisdom || sn == gsn_mass_owls_wisdom)
		{
			af.location  = APPLY_INT;
			af.duration = level * 10;
			af.modifier = spell_dice(ch, sn, 1, 4) + 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af);
			cnt++;

			send_to_char_color( "{138}Your eyes shine sagaciously as an owl's wisdom lights your path.\n\r", vch);
			act( "$n's {138}eyes shine sagaciously.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_divine_favor)
		{
			af.duration		= turn;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_LUCK_TOHIT;
			af.modifier 	= UMIN(level/3, 3);
			affect_join( ch, vch, &af);

			af.location 	= APPLY_LUCK_DAMG;
			af.modifier 	= UMIN(level/3, 3);
			affect_join( ch, vch, &af);
			
			act( "{138}You are filled with the favor of your deity.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n is filled with the favor of $s deity.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_divine_power)
		{
			af.duration		= turn;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_LUCK_TOHIT;
			af.modifier 	= UMIN(level/3, 6);
			affect_join( ch, vch, &af);

			af.location 	= APPLY_LUCK_DAMG;
			af.modifier 	= UMIN(level/3, 6);
			affect_join( ch, vch, &af);

			af.location 	= APPLY_LUCK_SKILL;
			af.modifier 	= UMIN(level/3, 6);
			affect_join( ch, vch, &af);

			af.location 	= APPLY_HIT;
			af.modifier 	= level;
			affect_join( ch, vch, &af);
			
			act( "{138}You are filled with the power of your deity.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n is filled with the power of $s deity.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_unholy_aura || sn == gsn_shield_of_law || sn == gsn_holy_aura || sn == gsn_cloak_of_chaos)
		{
			af.duration		= level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_DEFLECT;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_RES_SAVES;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_SAVE_MIND;
			af.modifier 	= 8;
			affect_join( ch, vch, &af);

			if (sn == gsn_unholy_aura)
				af.location 	= APPLY_SR_GOOD;
			else if (sn == gsn_holy_aura)
				af.location 	= APPLY_SR_EVIL;
			else if (sn == gsn_cloak_of_chaos)
				af.location 	= APPLY_SR_LAW;
			else
				af.location 	= APPLY_SR_CHAOS;
			af.modifier 	= 25;
			affect_join( ch, vch, &af);
			
			if (sn == gsn_unholy_aura)
			{
				af.location 	= APPLY_TURN_RESIST;
				af.modifier		= 4;
			}
			else if (sn == gsn_holy_aura)
			{
				af.location 	= APPLY_SAVE_NEGATIVE;
				af.modifier		= 4;
			}
			else if (sn == gsn_cloak_of_chaos)
			{
				af.location 	= APPLY_CONCEALMENT;
				af.modifier		= 25;
			}
			else
			{
				af.location 	= APPLY_FORTIFICATION;
				af.modifier		= 25;
			}
			affect_join( ch, vch, &af);
			
			if (sn == gsn_cloak_of_chaos)
			{
				act( "{058}A random pattern of color surrounds you.", vch, NULL, NULL, TO_CHAR);
				act( "{058}A random pattern of color surrounds $n.", vch, NULL, NULL, TO_ROOM);
			}
			if (sn == gsn_unholy_aura)
			{
				act( "{108}You are shrouded in a malevolent darkness.", vch, NULL, NULL, TO_CHAR);
				act( "{108}A malavolent darkness ensrouds $n.", vch, NULL, NULL, TO_ROOM);
			}
			if (sn == gsn_holy_aura)
			{
				act( "{178}A brilliant, divine radiance surrounds you.", vch, NULL, NULL, TO_CHAR);
				act( "{178}A brilliant, divine radiance surrounds $n.", vch, NULL, NULL, TO_ROOM);
			}
			if (sn == gsn_shield_of_law)
			{
				act( "{148}You are covered in a dim, blue aura.", vch, NULL, NULL, TO_CHAR);
				act( "{148}A dim blue aura radiates around $n.", vch, NULL, NULL, TO_ROOM);
			}
			cnt++;
		}
		if (sn == gsn_transformation)
		{
			af.duration		= level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_STR;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_DEX;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_CON;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_NATURAL_AC;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_COMP_FORT;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);
			
			act( "{138}Your muscles ripple as you thirst for battle!", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n's muscles ripple as $e thirsts for battle!", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_heroism)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_MOR_TOHIT;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_SAVES;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_SAVE_FEAR;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_SKILL;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);

			act( "{138}You brim with morale and bravery.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n brims with morale and bravery.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_greater_heroism)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_MOR_TOHIT;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_SAVES;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_MOR_SKILL;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_IMM_FEAR;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_HIT;
			af.modifier 	= level;
			affect_join( ch, vch, &af);
			
			act( "{138}You brim with morale and bravery.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n brims with morale and bravery.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_rage)
		{
			if (IS_AFFECTED(vch, AFF2_BERSERK))
			{
				act("$N is already enraged.", ch, NULL, vch, TO_CHAR);
				continue;
			}
			af.duration		= level;
			af.bittype  	= AFFECT_TO_CHAR;
			af.bitvector	= AFF2_BERSERK;
			af.location 	= APPLY_STR;
			af.modifier 	= UMIN(level/5+2, 4);
			affect_join( ch, vch, &af);

			af.location  = APPLY_CON;
			affect_to_char( ch, vch, &af );
		
			af.modifier  = af.modifier / 2;
			af.location  = APPLY_MOR_WILL;
			affect_to_char( ch, vch, &af );
		
			af.modifier  = -2;
			af.location  = APPLY_DODGE;
			affect_to_char( ch, vch, &af );
			
			act( "{118}You are filled with a seething rage!",vch, NULL, NULL, TO_CHAR);
			act( "$n {118}is filled with a seething rage!",vch, NULL, NULL, TO_ROOM);
			
			cnt += 3;
		}
		if (sn == gsn_protection_evil || sn == gsn_circle_against_evil)
		{
			af.duration  = sn == gsn_circle_against_evil ? 5*turn*level : turn*level;
			af.location  = APPLY_RES_EVIL;
			af.modifier  = 2;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af );

			af.location 	= APPLY_SAVE_MIND;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);
		
			act( "{138}You feel protected from evil.", vch, NULL, NULL, TO_CHAR );
			act( "{138}$n is shielded with a ward against evil.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_protection_good || sn == gsn_circle_against_good)
		{
			af.duration  = sn == gsn_circle_against_good ? 5*turn*level : turn*level;
			af.location  = APPLY_RES_GOOD;
			af.modifier  = 2;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af );
		
			af.location 	= APPLY_SAVE_MIND;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);
		
			act( "{118}You feel protected from good.", vch, NULL, NULL, TO_CHAR );
			act( "$n {118}is shielded with a ward against good.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_protection_law || sn == gsn_circle_against_law)
		{
			af.duration  = sn == gsn_circle_against_law ? 5*turn*level : turn*level;
			af.location  = APPLY_RES_LAW;
			af.modifier  = 2;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af );
		
			af.location 	= APPLY_SAVE_MIND;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);
		
			act( "{158}You feel protected from order.", vch, NULL, NULL, TO_CHAR );
			act( "{158}$n is shielded with a ward against order.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_protection_chaos || sn == gsn_circle_against_chaos)
		{
			af.duration  = sn == gsn_circle_against_chaos ? 5*turn*level : turn*level;
			af.location  = APPLY_RES_CHAOS;
			af.modifier  = 2;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af );
		
			af.location 	= APPLY_SAVE_MIND;
			af.modifier 	= 4;
			affect_join( ch, vch, &af);
		
			act( "{178}You feel protected from chaos.", vch, NULL, NULL, TO_CHAR );
			act( "{178}$n is shielded with a ward against chaos.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_undeath_ward)
		{
			af.duration  = turn * level;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			affect_join( ch, vch, &af );
		
			act( "{178}A veil of positive energy surrounds you.", vch, NULL, NULL, TO_CHAR );
			act( "{178}$n is veiled in positive energy.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_protection_spells)
		{
			af.duration  = hr * level;
			af.location  = APPLY_RES_SPELL;
			af.modifier  = 8;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_join( ch, vch, &af );
			
			act( "{138}You are warded against magical spells.", vch, NULL, NULL, TO_CHAR );
			if ( ch != vch )
				act( "{138}$N is warded against magical spells.", ch, NULL, vch, TO_CHAR );
			cnt++;
			cnt++;
			cnt++;
		}
		if (sn == gsn_missile_deflection)
		{
			af.duration  = turn * level;
			af.location  = APPLY_NONE;
			af.modifier  = level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_join( ch, vch, &af );
			
			act( "{138}You are warded against normal missiles.", vch, NULL, NULL, TO_CHAR );
			if ( ch != vch )
				act( "{138}$N is warded against normal missiles.", ch, NULL, vch, TO_CHAR );
		}
		if (sn == gsn_repulsion)
		{
			af.duration  = level;
			af.location  = APPLY_NONE;
			af.modifier  = level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_join( ch, vch, &af );
			
			act( "{138}You are warded against physical contact.", vch, NULL, NULL, TO_CHAR );
			if ( ch != vch )
				act( "{138}$N is warded against physical contact.", ch, NULL, vch, TO_CHAR );
		}
		if (sn == gsn_resistance)
		{
			af.duration  = turn;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			af.location  = APPLY_RES_SAVES;
			af.modifier  = 1;
			affect_join( ch, vch, &af );
		
			act( "{138}A magical ward surrounds $n.", vch, NULL, NULL, TO_ROOM );
			act( "{138}A magical ward surrounds you.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_slow)
		{
			if (is_affected(vch, gsn_haste))
			{
				affect_strip(vch, gsn_haste);
				cnt++;
				continue;
			}
			af.duration		= level;
			af.bittype  	= AFFECT_TO_CHAR;
			af.bitvector	= AFF2_STAGGERED;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_DODGE;
			af.modifier 	= -1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_SAVING_REFL;
			af.modifier 	= -1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_COMP_TOHIT;
			af.modifier 	= -1;
			affect_join( ch, vch, &af);

			act( "You slow down.", vch, NULL, NULL, TO_CHAR );
			act( "$n slows down.", vch, NULL, NULL, TO_ROOM );
			cnt++;
		}
		if (sn == gsn_virtue)
		{
			af.duration		= turn;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_HIT;
			af.modifier 	= spell_dice(ch, sn, 1, 3);
			affect_join( ch, vch, &af);
		
			act( "{138}A boon of virtue comes over $n.", vch, NULL, NULL, TO_ROOM );
			act( "{138}A boon of virtue comes over you.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_confusion || sn == gsn_lesser_confusion || sn == gsn_insanity || sn == gsn_symbol_of_insanity)
		{
			af.duration		= sn == gsn_confusion ? level : sn == gsn_lesser_confusion ? 1 : -1;
			af.bittype  	= AFFECT_TO_CHAR;
			af.bitvector	= AFF2_CONFUSION;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);
		
			send_to_char_color( "Your eyes glaze over in a confused stare.\n\r", vch );
			act ("$n's eyes glaze over with a confused stare.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_armor)
		{
			af.duration  	= hr * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_ARMOR;
			af.modifier 	= UMIN(level/3 + 1, 5);
			affect_join( ch, vch, &af);
		
			act( "{068}$n is encased in a {078}sh{068}im{078}me{068}ri{078}ng {068}au{078}ra {068}of magical armor.", vch, NULL, NULL, TO_ROOM );
			act( "{068}You are encased in a {078}sh{068}im{078}me{068}ri{078}ng {068}au{078}ra {068}of magical armor.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_dimensional_anchor)
		{
			af.duration  	= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);
		
			act( "{128}$n is covered in a shimmering emerald field.", vch, NULL, NULL, TO_ROOM );
			act( "{128}You are covered in a shimmering emerald field.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_shield_of_faith)
		{
			af.duration  	= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_DEFLECT;
			af.modifier 	= UMIN(level/6 + 2, 5);
			affect_join( ch, vch, &af);
		
			act( "{178}$n is surrounded by a deflective aura.", vch, NULL, NULL, TO_ROOM );
			act( "{178}You are surrounded by a deflective aura.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_armor_of_darkness)
		{
			af.duration  	= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_DEFLECT;
			af.modifier 	= 3 + UMIN(level/4, 5);
			affect_join( ch, vch, &af);

			af.location 	= APPLY_SAVE_LIGHT;
			af.modifier 	= 2;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_DARKVISION;
			af.modifier 	= 60;
			affect_join( ch, vch, &af);

			act( "{108}A shroud of shadows envelops $n.", vch, NULL, NULL, TO_ROOM );
			act( "{108}You are enveloped in a shroud of shadow.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_barkskin)
		{
			af.level    	= 10 * turn * level;
			af.duration		= turn;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_NATURAL_AC;
			af.modifier 	= UMIN(level/3 + 1, 5);
			affect_join( ch, vch, &af);
		
			act( "{038}$n's skin thickens into the texture of bark.", vch, NULL, NULL, TO_ROOM );
			act( "{038}Your skin thickens into the texture of bark.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_true_seeing)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_CHAR;
			af.bitvector	= AFF_TRUESIGHT;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);
		
			send_to_char_color( "{138}Your eyes gain the ability to see through the facades over reality.\n\r", vch );
		}
		if (sn == gsn_feather_fall)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_CHAR;
			af.bitvector	= AFF_FEATHER_FALL;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);
		
			send_to_char_color( "{178}You feel light as a feather.\n\r", vch );
			act( "{178}$n becomes light as a feather.", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_globe_of_invulnerability || sn == gsn_minor_globe)
		{
			af.duration		= level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);
		
			act( "{178}A {078}sh{178}im{078}me{178}ri{078}ng {178}g{078}lo{178}be {078}forms around you.\n\r", vch, NULL, NULL, TO_CHAR );
			act( "{178}A {078}sh{178}im{078}me{178}ri{078}ng {178}g{078}lo{178}be {078}forms around $n.\n\r", vch, NULL, NULL, TO_ROOM );
		}
		if (sn == gsn_analyze_dweomer)
		{
			af.duration		= level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);
		
			send_to_char_color( "{148}Your eyes attune to all things magical.\n\r", vch );
		}
		if (sn == gsn_leap)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_JUMP;
			af.modifier 	= UMIN(9 + level, 20);
			affect_join( ch, vch, &af);
		
			act( "{138}Your legs grow stronger.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n's legs grow stronger.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_spider_climb)
		{
			af.duration		= hr * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_CLIMB;
			af.modifier 	= UMIN(9 + level, 20);
			affect_join( ch, vch, &af);
		
			act( "{138}You gain the ability to walk on walls.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n gains the ability to walk on walls.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_moment_of_prescience)
		{
			af.duration		= hr * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_INS_SKILL;
			af.modifier 	= UMIN(level, 25);
			affect_join( ch, vch, &af);
		
			act( "{138}Your mind is guided by a powerful insight.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n's face radiates with powerful insight.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_true_strike)
		{
			af.duration		= turn;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_INS_TOHIT;
			af.modifier 	= 20;
			affect_join( ch, vch, &af);
		
			act( "{138}Your aim becomes deadly.", vch, NULL, NULL, TO_CHAR);
			if (ch != vch)
				act( "{138}$N's aim becomes deadly.", ch, NULL, vch, TO_CHAR);
		}
		if (sn == gsn_wraith_form)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_DR_MAGIC;
			af.modifier 	= 5;
			affect_join( ch, vch, &af);
		
			act( "{108}$n's visage assumes a ghostly form.", vch, NULL, NULL, TO_ROOM );
			act( "{108}Your visage assumes a ghostly form.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_displacement)
		{
			af.duration		= level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_CONCEALMENT;
			af.modifier 	= 50;
			affect_join( ch, vch, &af);
		
			act( "{168}$n's image shifts two feet away.", vch, NULL, NULL, TO_ROOM );
			act( "{168}Your image is displaced.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_blur)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_CONCEALMENT;
			af.modifier 	= 20;
			affect_join( ch, vch, &af);
		
			act( "{108}$n's image blurs and distorts.", vch, NULL, NULL, TO_ROOM );
			act( "{108}Your image becomes blurred.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_spell_resistance)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_SPELL_RES;
			af.modifier 	= 12 + level;
			affect_join( ch, vch, &af);
		
			act( "{138}An anti-magical ward glows around $n.", vch, NULL, NULL, TO_ROOM );
			act( "{138}An anti-magical ward glows around you.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_tree_stride)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);
		
			act( "{128}You gain the ability to walk the trees.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_detect_scrying)
		{
			af.duration		= hr * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);
		
			act( "{178}Your mind awakens to prying eyes.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_discern_lies)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_SENSE_MOT;
			af.modifier 	= level / 2;
			affect_join( ch, vch, &af);
		
			act( "{178}Your eyes better perceive the truth.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_speak_w_animals)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_HANDLE_ANIM;
			af.modifier 	= level / 5 + 4;
			affect_join( ch, vch, &af);
		
			act( "{178}Your mind opens to the fauna around you.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_regenerate)
		{
			af.duration		= level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_REGENERATION;
			af.modifier 	= level / 5 + 3;
			affect_join( ch, vch, &af);
		
			act( "{138}$n's wounds start to knit themselves.", vch, NULL, NULL, TO_ROOM );
			act( "{138}Your wounds start to knit rapidly.", vch, NULL, NULL, TO_CHAR );
		}
		if (sn == gsn_death_ward || sn == gsn_mass_death_ward)
		{
			af.duration  = turn * level;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.location  = APPLY_SAVE_DEATH;
			af.modifier  = 4 + UMIN(level/4, 4);
			affect_join( ch, vch, &af );
			
			af.location  = APPLY_IMM_NEGATIVE;
			af.modifier  = 1;
			affect_join( ch, vch, &af );
			cnt++;
		
			act( "{108}$n is warded against the sting of death.", vch, NULL, NULL, TO_ROOM );
			act( "{108}You are warded against the sting of death.", vch, NULL, NULL, TO_CHAR );
		}
		// Add here because damage shields should not stack - Kregor
		if (!is_affected(vch, gsn_blade_barrier) && !is_affected(vch, gsn_fire_shield) 
		&& !is_affected(vch, gsn_thorn_body) && !is_affected(vch, gsn_aura_of_retribution) 
		&& !is_affected(vch, gsn_death_armor))
		{
			if (sn == gsn_blade_barrier)
			{
				af.duration  = turn * level;
				af.location  = APPLY_DEFLECT;
				af.modifier  = 4;
				af.bittype   = AFFECT_TO_NONE;
				af.bitvector = AFF_NONE;
				affect_join( ch, vch, &af );
			
				act( "{178}Blades of force whirl around $n!", vch, NULL, NULL, TO_ROOM );
				act( "{178}You are surrounded by whirling blades of force!", vch, NULL, NULL, TO_CHAR );
			}
			if (sn == gsn_fire_shield)
			{
				af.duration  = turn * level;
				af.location  = APPLY_DR_FIRE;
				af.modifier  = 10;
				af.bittype   = AFFECT_TO_NONE;
				af.bitvector = AFF_NONE;
				affect_join( ch, vch, &af );
			
				act( "$n {118}is enveloped in a shield of flames!", vch, NULL, NULL, TO_ROOM );
				send_to_char_color( "{118}You are enveloped in a shield of flames!\n\r", vch );
			}
			if (sn == gsn_thorn_body)
			{
				af.duration  = turn * level;
				af.location  = APPLY_NONE;
				af.modifier	 = 0;
				af.bittype   = AFFECT_TO_NONE;
				af.bitvector = AFF_NONE;
				affect_join( ch, vch, &af );
			
				act( "{038}Thorns sprout from $n's body!", vch, NULL, NULL, TO_ROOM );
				send_to_char_color( "{038}Thorns sprout from your body!\n\r", vch );
			}
			if (sn == gsn_aura_of_retribution)
			{
				af.duration  = turn * level;
				af.location  = 0;
				af.modifier  = 0;
				af.bittype   = AFFECT_TO_NONE;
				af.bitvector = AFF_NONE;
				affect_join( ch, vch, &af );
			
				act( "$n {138}is enveloped in a rediating aura.", vch, NULL, NULL, TO_ROOM );
				send_to_char_color( "{138}You are enveloped in radiating aura.\n\r", vch );
			}
			if (sn == gsn_death_armor)
			{
				af.duration  = turn * level;
				af.location  = 0;
				af.modifier  = 0;
				af.bittype   = AFFECT_TO_NONE;
				af.bitvector = AFF_NONE;
				affect_join( ch, vch, &af );
			
				act( "$n {108}is enveloped in a black aura!", vch, NULL, NULL, TO_ROOM );
				send_to_char_color( "{108}You are enveloped in a black aura!\n\r", vch );
			}
		}
		if (sn == gsn_spirit_of_triumph)
		{
			af.duration  = level;
			af.location  = APPLY_LUCK_TOHIT;
			af.modifier  = level/2;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_join( ch, vch, &af );
		
			af.location  = APPLY_LUCK_DAMG;
			af.modifier  = level/2;
			affect_join( ch, vch, &af );

			af.location  = APPLY_LUCK_SAVES;
			af.modifier  = level/2;
			affect_join( ch, vch, &af );

			af.location  = APPLY_LUCK_SKILL;
			af.modifier  = level/2;
			affect_join( ch, vch, &af );

			act( "$n {178}is empowered by a blessing of great favor!", vch, NULL, NULL, TO_ROOM );
			send_to_char_color( "{178}You are empowered by a blessing of great favor!\n\r", vch );
		}
		if (sn == gsn_magic_fang)
		{
			if (!has_natural_weapon(vch) && !learned(ch, gsn_martial_arts))
			{
				act( "$N has no natural weapons to enhance.", ch, NULL, vch, TO_CHAR);
				pop_call();
				return FALSE;
			}

			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_HITROLL;
			af.modifier 	= URANGE(1, level/4, 5);
			affect_join( ch, vch, &af);

			af.location 	= APPLY_DAMROLL;
			af.modifier 	= URANGE(1, level/4, 5);
			affect_join( ch, vch, &af);
			
			act( "{138}Your natural weapons glow faintly.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n's natural weapons glow faintly.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_stone_fist)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_NONE;
			af.modifier 	= 0;
			affect_join( ch, vch, &af);

			act( "{108}Your limbs harden like stone.", vch, NULL, NULL, TO_CHAR);
			act( "{108}$n's limbs become stoney.", vch, NULL, NULL, TO_ROOM);
		}
		if (sn == gsn_undeaths_eternal_foe)
		{
			af.duration		= turn * level;
			af.bittype  	= AFFECT_TO_NONE;
			af.bitvector	= AFF_NONE;
			af.location 	= APPLY_IMM_NEGATIVE;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_IMM_POISON;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);

			af.location 	= APPLY_IMM_DISEASE;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);
			
			af.location 	= APPLY_IMM_PARALYSIS;
			af.modifier 	= 1;
			affect_join( ch, vch, &af);

			af.location  = APPLY_SAVE_DEATH;
			af.modifier  = 4 + UMIN(level/4, 4);
			affect_join( ch, vch, &af );

			act( "{138}You are wrapped in an aura of positive energy.", vch, NULL, NULL, TO_CHAR);
			act( "{138}$n is is wrapped in an aura of positive energy.", vch, NULL, NULL, TO_ROOM);
		}
		if (!area)
			break;
	}
	
	pop_call();
	return TRUE;
}


DO_SPELL(spell_acid_arrow)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	int dam;

	push_call("spell_acid_arrow(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	dam = spell_dice(ch, sn, 2, 4);

	if (valid_victim(victim) && !save_resist(ch, victim, sn, level))
	{
		damage( ch, victim, dam, sn, NULL );
		
		af.type      = sn;
		af.duration  = 1 + (level/3);
		af.location  = 0;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;
		affect_to_char( ch, victim, &af );
		
		act( "{128}Caustic acid begins to eat away at $n.", victim, NULL, NULL, TO_ROOM);
		act( "{128}Caustic acid begins to eat away at you!", victim, NULL, NULL, TO_CHAR);
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_acid_splash)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_acid_splash(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act( "{128}A tiny orb of acid shoots from $n's hand.", ch, NULL, NULL, TO_ROOM );
	act( "{128}A tiny orb of acid shoots from your hand.", ch, NULL, NULL, TO_CHAR );

	dam  = spell_dice(ch, sn, 1,4) + UMIN(level/2, 5);

	spell_damage(ch, victim, dam, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_admonishing_ray)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int count;

	push_call("spell_admonishing_ray(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (count = 0; count <= UMIN(level, 11); count++)
	{
		if ( count == 1 || (count - 3) % 4 == 0 )
		{
			act( "{118}A ray of force shoots from $n's finger.", ch, NULL, NULL, TO_ROOM );
			act( "{118}A ray of force shoots from your finger.", ch, NULL, NULL, TO_CHAR );
	
			spell_damage(ch, victim, spell_dice(ch, sn, 4, 6), sn, level);
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_align_weapon)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;
	int alignment = 0;

	push_call("spell_align_weapon(%p,%p,%p,%p)",sn,level,ch,vo);

	if (obj->item_type != ITEM_WEAPON)
	{
		act( "You can only imbue weapons with this blessing.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (IS_UNCONCERNED(ch) && IS_NEUTRAL(ch))
	{
		send_to_char("You lack any moral or ethic to align a weapon.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (IS_CHAOTIC(ch) && !IS_OBJ_CHAOTIC(obj))
		SET_BIT(alignment, ITEM_CHAOTIC);
	if (IS_LAWFUL(ch) && !IS_OBJ_LAWFUL(obj))
		SET_BIT(alignment, ITEM_LAWFUL);
	if (IS_GOOD(ch) && !IS_OBJ_GOOD(obj))
		SET_BIT(alignment, ITEM_GOOD);
	if (IS_EVIL(ch) && !IS_OBJ_EVIL(obj))
		SET_BIT(alignment, ITEM_EVIL);
		
	if (alignment == 0)
	{
		act("You failed to add any moral taint to $p.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return TRUE;
	}

	act( "{178}Divine power radiates from $p.", ch, obj, NULL, TO_ALL);

	af.type      = sn;
	af.duration  = level;
	af.location  = 0;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_OBJ;
	af.bitvector = alignment;
	af.level     = level;
	affect_to_obj( ch,obj, &af);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_animate_dead)
{
	CHAR_DATA *mh;
	MOB_INDEX_DATA *mob;
	OBJ_DATA	*obj;

	push_call("spell_animate_dead(%p,%p,%p,%p)",sn,level,ch,vo);

	for (obj = ch->in_room->first_content ; obj ; obj = obj->next_content)
	{
		if (obj->item_type == ITEM_CORPSE_NPC && obj->level <= level)
		{
			break;
		}
	}

	if (obj == NULL)
	{
		send_to_char( "You find no suitable corpse.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if ((mob = get_mob_index(obj->value[4])) == NULL)
	{
		send_to_char( "You cannot animate that corpse.\n\r", ch);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (mob->level > level)
	{
		send_to_char( "You are not strong enough to animate that corpse.\n\r", ch);
		pop_call();
		return FALSE;
	}

	mh = create_mobile(mob_index[MOB_VNUM_ZOMBIE]);
	char_to_room( mh, ch->in_room->vnum, TRUE );
	
	if (race_table[mob->race].body_type != BTYPE_BIPEDAL)
	{
		mh->race = RACE_ZOMBIE_ANIMAL;
	}

	mh->level				= race_table[mob->race].hit_dice;
	mh->size				= mob->size;
	mh->perm_dex		= mob->perm_dex - 2;
	mh->perm_str		= mob->perm_str + 2;
	mh->max_hit			= mh->level * dice(1,8);
	
	if (arcane_mastery(ch, SCHOOL_NECROMANCY))
	{
		mh->perm_str += 4;
		mh->max_hit  += mh->level * 2;
		
		AFFECT_DATA af;
		af.type      = sn;
		af.duration  = -1;
		af.location  = APPLY_TURN_RESIST;
		af.modifier  = 4;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;
		affect_to_char( ch, mh, &af );
	}
		
	mh->hit					= get_max_hit(mh);
	
	switch(mh->size)
	{
		case SIZE_SMALL:
			mh->nat_armor += 1;
			break;
		case SIZE_MEDIUM:
			mh->nat_armor += 2;
			mh->level += 1;
			break;
		case SIZE_LARGE:
			mh->nat_armor += 3;
			mh->level += 2;
			break;
		case SIZE_HUGE:
			mh->nat_armor += 4;
			mh->level += 4;
			break;
		case SIZE_GARGANTUAN:
			mh->nat_armor += 7;
			mh->level += 6;
			break;
		case SIZE_COLOSSAL:
			mh->nat_armor += 11;
			mh->level += 10;
			break;
		default:
			break;
	}

	SET_BIT( mh->affected_by , AFF_DOMINATE );

	act( "{108}$p rises up into undeath.", ch, obj, NULL, TO_ALL );

	RESTRING(mh->name, obj->name);
	RESTRING(mh->short_descr, obj->short_descr);
	RESTRING(mh->long_descr, format("%s is here, perpetuated by undeath.", obj->short_descr));
	RESTRING(mh->description, format("%s%s", obj->description, "It's form is now tattered and twisted through undeath.\n"));

	while (obj->first_content)
	{
		obj->first_content->sac_timer = OBJ_SAC_TIME;
		obj_to_room(obj->first_content, obj->in_room->vnum);
	}
	junk_obj(obj);

	add_follower( mh , ch );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_animate_object)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	MOB_INDEX_DATA *pMob;
	CHAR_DATA *mh;
	int mod, size;
	char clr[10];

	push_call("spell_animate_object(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!IS_SET(obj->wear_flags, CAN_WEAR_TAKE))
	{
		act("$p is rooted too firmly to the ground.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (IS_OBJ_STAT(obj, ITEM_MAGIC))
	{
		act("$p is already enchanted.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (obj->owned_by)
	{
		act("You cannot seem to animate $p.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (obj->size < SIZE_TINY)
	{
		act("$p is too small to animate.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	mod = obj->size - SIZE_TINY;
	
	switch (mod)
	{
		case 0:
		case 1:
			size = 1;
			break;
		case 2:
			size = 2;
			break;
		case 3:
			size = 4;
			break;
		case 4:
			size = 8;
			break;
		case 5:
			size = 16;
			break;
		default:
			size = 32;
			break;
	}
	
	if (level < size)
	{
		act("$p is too big for you to animate.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if ((pMob = get_mob_index(MOB_VNUM_SUMMONS)) == NULL)
	{
		act("You cannot seem to animate $p.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	mh = create_mobile( pMob );
	char_to_room( mh, ch->in_room->vnum, TRUE );
	mh->npcdata->sac_timer = level;
	mh->npcdata->sac_string = STRALLOC("$n falls to the ground lifeless once more.");
	
	mh->race				= RACE_ANIMATED_OBJ;
	mh->level				= UMAX(1, mod * 2);
	mh->size				= obj->size;
	mh->perm_str		= 10 + race_table[mh->race].race_mod[0];
	mh->perm_dex		= 10 + race_table[mh->race].race_mod[1];
	mh->perm_con		= 10 + race_table[mh->race].race_mod[2];
	mh->perm_int		= 10 + race_table[mh->race].race_mod[3];
	mh->perm_wis		= 10 + race_table[mh->race].race_mod[4];
	mh->perm_cha		= 10 + race_table[mh->race].race_mod[5];
	mh->height			= race_table[mh->race].height;
	mh->weight			= race_table[mh->race].weight;
	mh->speak				= race_table[mh->race].speaks;
	mh->language		= race_table[mh->race].understands;
	mh->alignment = mh->ethos = 0;

	if (mod)
	{
		mh->height    *= 2 * mod;
		mh->weight    *= 8 * mod;
	}
	
	mh->perm_str  	+= mod * 4;
	mh->perm_con  	+= mod * 2;
	mh->perm_dex		+= mod * 2;
	mh->max_hit			= dice(mh->level, race_type_table[race_table[mh->race].type].hit_die);
	mh->max_hit			= UMAX(mh->level * race_type_table[race_table[mh->race].type].hit_die / 2, mh->max_hit);
	mh->max_hit			+= mod * 10;
	mh->hit					= get_max_hit(mh);

	// just to be thorough, copy the first color tag from obj to mob - Kregor
	if (strstr(obj->short_descr, "{"))
	{
		if (strstr(obj->short_descr, "{10"))
			strcpy(clr, ansi_translate("{108}"));
		else if (strstr(obj->short_descr, "{11"))
			strcpy(clr, ansi_translate("{118}"));
		else if (strstr(obj->short_descr, "{12"))
			strcpy(clr, ansi_translate("{128}"));
		else if (strstr(obj->short_descr, "{13"))
			strcpy(clr, ansi_translate("{138}"));
		else if (strstr(obj->short_descr, "{14"))
			strcpy(clr, ansi_translate("{148}"));
		else if (strstr(obj->short_descr, "{15"))
			strcpy(clr, ansi_translate("{158}"));
		else if (strstr(obj->short_descr, "{16"))
			strcpy(clr, ansi_translate("{168}"));
		else if (strstr(obj->short_descr, "{17"))
			strcpy(clr, ansi_translate("{178}"));
		else if (strstr(obj->short_descr, "{01"))
			strcpy(clr, ansi_translate("{018}"));
		else if (strstr(obj->short_descr, "{02"))
			strcpy(clr, ansi_translate("{028}"));
		else if (strstr(obj->short_descr, "{03"))
			strcpy(clr, ansi_translate("{038}"));
		else if (strstr(obj->short_descr, "{04"))
			strcpy(clr, ansi_translate("{048}"));
		else if (strstr(obj->short_descr, "{05"))
			strcpy(clr, ansi_translate("{058}"));
		else if (strstr(obj->short_descr, "{06"))
			strcpy(clr, ansi_translate("{068}"));
		else if (strstr(obj->short_descr, "{07"))
			strcpy(clr, ansi_translate("{078}"));
	}
	RESTRING(mh->name, format("%s animated", obj->name));
	RESTRING(mh->short_descr, format("an animated %s", short_to_name(obj->short_descr, 1)));
	RESTRING(mh->long_descr, format("{078}An animated %s%s {078}is here.", clr, short_to_name(obj->short_descr, 1)));

	SET_BIT( mh->affected_by , AFF_DOMINATE );

	act( "$n begins to move on its own.", mh, NULL, NULL, TO_ROOM );

	junk_obj(obj);
	
	add_follower( mh , ch );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_antimagic_field)
{
	ROOM_INDEX_DATA *room;
	CHAR_DATA *rch, *rch_next;
	AFFECT_DATA *paf, *paf_next;
	ROOM_TIMER_DATA rtd;
	OBJ_DATA *obj;
	int paf_type;

	push_call("spell_antimagic_field(%p,%p,%p,%p)",sn,level,ch,vo);

	if( ch->in_room == NULL )
	{
		pop_call();
		return FALSE;
	}

	room = ch->in_room;

	if( IS_SET(room->room_flags, ROOM_NO_MAGIC ) )
	{
		send_to_char( "There is already an antimagic ward here.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if( IS_SET(room->room_flags, ROOM_SAFE ) )
	{
		send_to_char( "Your antimagic ward dissipates immediately.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= level * turn;
	rtd.bitvector	= ROOM_NO_MAGIC;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{058}A sphere of antimagic energy covers the area.", ch, NULL, NULL, TO_ALL );

	for (rch = ch->in_room->first_person ; rch != NULL ; rch = rch_next)
	{
		rch_next = rch->next_in_room;
	
		for ( paf_type = 0, paf = rch->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;
	
			if (!is_spell(paf->type))
			{
				continue;
			}
			if ((paf_type == 0 || paf_type != paf->type) && paf->duration >= 0)
			{
				if (skill_table[paf->type].msg_off)
				{
					act( skill_table[paf->type].msg_off, rch, NULL, NULL, TO_CHAR);
				}
				if (skill_table[paf->type].msg_off_room)
				{
					act(skill_table[paf->type].msg_off_room, rch, NULL, NULL, TO_ROOM);
				}
			}
			paf_type = paf->type;
			affect_from_char(rch, paf);
		}
		for (obj = rch->first_carrying; obj != NULL ; obj = obj->next_content)
		{
			for ( paf = obj->first_affect; paf != NULL; paf = paf_next )
			{
				paf_next = paf->next;
	
				if (!is_spell(paf->type))
				{
					continue;
				}
				if ((paf_type == 0 || paf_type != paf->type) && paf->duration >= 0)
				{
					if (skill_table[paf->type].msg_obj_off)
					{
						act( skill_table[paf->type].msg_obj_off, rch, obj, NULL, TO_CHAR);
						if (rch->in_room)
							act( skill_table[paf->type].msg_obj_off, rch, obj, NULL, TO_ROOM);
					}
				}
				paf_type = paf->type;
				affect_from_obj(obj, paf);
			}
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_arcane_eye)
{
	MOB_INDEX_DATA *pMob;
	CHAR_DATA *mh;

	push_call("spell_arcane_eye(%p,%p,%p,%p)",sn,level,ch,vo);

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

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act( "$n seems to enter into a trance", ch, NULL, NULL, TO_ROOM);

	pMob	= get_mob_index( MOB_VNUM_WIZARD_EYE );   /* Hard coded in mud.are */
	mh	= create_mobile( pMob );
	char_to_room(mh, ch->in_room->vnum, TRUE);

	ch->desc->character 	= mh;
	ch->desc->original  	= ch;
	mh->desc            	= ch->desc;
	ch->desc            	= NULL;
	ch->pcdata->switched	= TRUE;
	mh->npcdata->sac_timer = turn * level;
	vt100prompt( mh );

	act( "You focus your senses elsewhere.", mh, NULL, NULL, TO_CHAR);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_banish)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *rch;
	ROOM_INDEX_DATA *pRoomIndex;
	int cnt = 0;

	push_call("spell_banish(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (victim->in_room == NULL
	||  victim == ch
	||  IS_SET(ch->in_room->room_flags, ROOM_NO_RECALL)
	||  IS_SET(ch->in_room->area->flags, AFLAG_NORECALL)
	||  IS_SET(ch->in_room->room_flags,  AFLAG_NOSUMMON)
	||  IS_SET(ch->in_room->room_flags,  AFLAG_NOTELEPORT)
	||	victim->level > level * 2
	||  save_resist(ch, victim, sn, level))
	{
		act( "You failed to banish $N.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return TRUE;
	}

	if (IS_NPC(victim) && race_type(victim) == RTYPE_OUTSIDER)
	{	
		act( "{138}$n is banished from this plane!", victim, NULL, NULL, TO_ROOM );
		act( "{138}You have been banished from this plane!", ch, NULL, victim, TO_VICT );
		if (victim->in_room != ch->in_room)
			act( "{138}$N is banished from this plane!", ch, NULL, victim, TO_ROOM );

		extract_char(victim);
		pop_call();
		return TRUE;
	}
	
	for ( ; ; )
	{
		/*
			Better safe than freezing, added cnt - Scandum 01-04-2002
		*/
		if (cnt++ > 10000)
		{
			send_to_char("Nothing happens", ch);
			pop_call();
			return TRUE;
		}
		pRoomIndex = get_room_index(number_range(victim->in_room->area->low_r_vnum, victim->in_room->area->hi_r_vnum));

		if ( pRoomIndex != NULL )
		{
			if (!is_room_good_for_teleport(victim, pRoomIndex->vnum)
			||  pRoomIndex == victim->in_room)
			{
				continue;
			}
			for (rch = pRoomIndex->first_person ; rch ; rch = rch->next_in_room)
			{
				if (IS_NPC(rch) && rch->pIndexData->pShop != NULL)
				{
					break;
				}
			}
			if (rch)
			{
				continue;
			}
			break;
		}
	}

	act( "{138}$n is banished from your presence.", victim, NULL, NULL, TO_ROOM );
	act( "{138}You have been banished from $n's presence.", ch, NULL, victim, TO_VICT );
	if (victim->in_room != ch->in_room)
		act( "{138}$N is banished from your presence.", ch, NULL, victim, TO_ROOM );

	if (in_combat(victim))
	{
		withdraw_combat(victim);
	}
	char_from_room( victim );
	char_to_room( victim, pRoomIndex->vnum, TRUE );

	do_look( victim, "auto" );

	if (!IS_NPC(victim))
	{
		mprog_greet_trigger(victim);
		oprog_greet_trigger(victim);
		rprog_greet_trigger(ch);
	}	

	pop_call();
	return TRUE;
}

DO_SPELL(spell_black_tentacles)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	ROOM_TIMER_DATA rtd;

	push_call("spell_black_tentacles(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(victim->in_room->room_flags, ROOM_ENTANGLE))
	{
		send_to_char("This area is already affected.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (IS_SET(victim->in_room->room_flags, ROOM_SAFE))
	{
		send_to_char_color( "Wards prevent you from casting that here.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= victim->in_room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= level;
	rtd.bitvector	= ROOM_ENTANGLE;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{108}Large, black tentacles erupt from the ground!", victim, NULL, NULL, TO_CHAR);
	act( "{108}Large, black tentacles erupt from the ground!", victim, NULL, NULL, TO_ROOM);

	if (ch->in_room != victim->in_room)
	{
		act( "{108}You conjure up black tentacles around $N!", ch, NULL, victim, TO_CHAR);
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_blacklight)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	ROOM_TIMER_DATA rtd;
	
	push_call("spell_blacklight(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(victim->in_room->room_flags, ROOM_BLACKLIGHT))
	{
		send_to_char("This room is already shrouded in darkness.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (IS_SET(victim->in_room->room_flags, ROOM_SAFE) || IS_SET(ch->in_room->room_flags, ROOM_SAFE))
	{
		send_to_char( "Wards prevent you from casting that spell.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (is_room_affected(victim->in_room, gsn_daylight))
	{
		affect_room_strip(victim->in_room, gsn_daylight);
		pop_call();
		return TRUE;
	}

	rtd.vnum			= victim->in_room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_ROOM_LIGHT;
	rtd.modifier	= -2;
	rtd.duration	= level;
	rtd.bitvector	= ROOM_BLACKLIGHT;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{108}The light around you dissolves into darkness.", victim, NULL, NULL, TO_ALL);
	if (ch->in_room != victim->in_room)
		act( "{108}The light around $N dissolves into darkness.", ch, NULL, victim, TO_CHAR);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_break_enchantment)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *fch;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	int paf_type, diceroll, count;

	push_call("spell_break_enchantment(%p,%p,%p,%p)",sn,level,ch,vo);

	if (arcane_mastery(ch, SCHOOL_ABJURATION))
		diceroll = UMAX(dice(1,20), dice(1,20)) + UMIN(level, 15);
	else
		diceroll = dice(1, 20) + UMIN(level, 15);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (count = 0, fch = victim->in_room->first_person ; fch ; fch = fch->next_in_room)
	{
		if (count >= level)
			continue;
		
		if (!is_same_group(victim, fch))
			continue;
		
		for ( paf_type = 0, paf = fch->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;

			if (!is_spell(paf->type))
				continue;
				
			if (!IS_SET(paf->bitvector, AFF_BLIND)
			&& !IS_SET(paf->bitvector, AFF2_PARALYSIS) 
			&& !IS_SET(paf->bitvector, AFF_SLEEP) 
			&& !IS_SET(paf->bitvector, AFF_CURSE) 
			&& !IS_SET(paf->bitvector, AFF_DEAF) 
			&& !IS_SET(paf->bitvector, AFF_DOMINATE) 
			&& !IS_SET(paf->bitvector, AFF2_FASCINATED) 
			&& !IS_SET(paf->bitvector, AFF2_HALLUCINATE) 
			&& !IS_SET(paf->bitvector, AFF2_STUNNED) 
			&& !IS_SET(paf->bitvector, AFF2_POSSESS) 
			&& !IS_SET(paf->bitvector, AFF2_SILENCE) 
			&& !IS_SET(paf->bitvector, AFF2_CONFUSION) 
			&& !IS_SET(paf->bitvector, AFF2_PETRIFICATION))
			{
				if (skill_table[paf->type].spell_class != STYPE_CURSE)
				{
					continue;
				}
			}

			if (diceroll < 11 + paf->level)
				continue;
			
			if (skill_table[paf->type].msg_off)
			{
				act( skill_table[paf->type].msg_off, fch, NULL, NULL, TO_CHAR);
			}
			if (skill_table[paf->type].msg_off_room)
			{
				act( skill_table[paf->type].msg_off_room, fch, NULL, NULL, TO_ROOM);
			}

			paf_type = paf->type;
			affect_from_char(fch, paf);
		}

		if (paf_type == 0)
		{
			send_to_char( "Nothing appears to happen.\n\r", ch );
		}
		else if (fch != ch)
		{
			act( "$N's enchantments vanish one by one.", ch, NULL, fch, TO_CHAR );
		}
		for (obj = fch->first_carrying; obj!=NULL ; obj=obj->next_content)
		{
			if(IS_SET(obj->extra_flags, ITEM_NOREMOVE))
			{
				act( "%p is removed from $n.", fch, obj, NULL, TO_ROOM);
				act( "%p is removed from you.", fch, obj, NULL, TO_CHAR);
				obj_from_char(obj);
				obj_to_room( obj, fch->in_room->vnum );
			}
		}
		count += 1;
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_burning_hands)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_burning_hands(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act( "{118}Searing flames shoot from $n's hands.", ch, NULL, NULL, TO_ROOM );
	act( "{118}Searing flames shoot from your hands.", ch, NULL, NULL, TO_CHAR );

	dam  = spell_dice(ch, sn, UMIN(level, 5),4);

	spell_damage(ch, victim, dam, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_scorching_ray)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int count;

	push_call("spell_scorching_ray(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (count = 0; count <= UMIN(level, 11); count++)
	{
		if ( count == 1 || (count - 3) % 4 == 0 )
		{
			act( "{118}A ray of flames shoots from $n's finger.", ch, NULL, NULL, TO_ROOM );
			act( "{118}A ray of flames shoots from your finger.", ch, NULL, NULL, TO_CHAR );

			spell_damage(ch, victim, spell_dice(ch, sn, 4, 6), sn, level);
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_call_lightning)
{
	ROOM_INDEX_DATA *room;
	ROOM_TIMER_DATA rtd;

	push_call("spell_call_lightning(%p,%p,%p,%p)",sn,level,ch,vo);

	if ((room = ch->in_room) == NULL)
	{
		pop_call();
		return FALSE;
	}
	
	if (NO_WEATHER_SECT(room->sector_type))
	{
		send_to_char_color( "{048}You cannot see the weather from here.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= sn = gsn_call_lightning ? 10 : 15;
	rtd.bitvector	= ROOM_NONE;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{108}Thunder clouds quickly gather in the area above.", ch, NULL, NULL, TO_ALL);

	pop_call();
	return TRUE;
}

/*
 * One function for all Calm X spells - Kregor
 */
DO_SPELL(spell_calm)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;

	push_call("spell_calm(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;
		
		if (!can_mass_cast(ch, vch, sn))
			continue;
			
		if (is_same_group(ch, vch))
			continue;

		if (sn == gsn_calm_animals && !IS_ANIMAL(ch))
			continue;
			
		if (!in_combat(vch) && !IS_ACT(vch, ACT_AGGRESSIVE))
			continue;
			
		if (save_resist(ch, vch, sn, level) != FALSE)
			continue;
			
		if (in_combat(vch))
			char_from_combat(vch);

		af.type      = sn;
		af.duration  = turn * level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_CALMED;
		af.level     = level;
		affect_join( ch, vch, &af );
		
		act("Your emotions are calmed.", vch, NULL, NULL, TO_CHAR);
		act("$n settles as a soothing peace comes over $m", vch, NULL, NULL, TO_ROOM);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_cause_fear)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	sh_int count, save, duration, mod;

	push_call("spell_cause_fear(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (count = 0, vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (count && count >= level / 3)
			break;

		if (sn == gsn_cause_fear)
		{
			if (vch != victim)
			{
				continue;
			}
		}
		else if (!can_mass_cast(ch, vch, sn))
		{
			continue;
		}
	
		if ((sn == gsn_cause_fear || sn == gsn_scare) && vch->level > 6)
		{
			act( "You ignore $n's fear spell.", vch, NULL, ch, TO_CHAR );
			act( "$n shrugs off your spell.", vch, NULL, NULL, TO_ROOM );
			continue;
		}
	
		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
		{
			continue;
		}
			
		if (IS_NPC(vch))
		{
			if (!IS_SET(vch->act, ACT_WIMPY))
				SET_BIT(vch->act, ACT_WIMPY);
		}
		
		if (!save)
		{
			duration = sn == gsn_fear ? level : dice(1,4);
			mod = sn == gsn_fear ? 3 : 2;
			act( "You become filled with fear!", vch, NULL, NULL, TO_CHAR );
			act( "$n becomes filled with fear!", vch, NULL, NULL, TO_ROOM );
		}
		else
		{
			duration = mod = 1;
			act( "You become shaken!", vch, NULL, NULL, TO_CHAR );
			act( "$n becomes shaken!", vch, NULL, NULL, TO_ROOM );
		}
	
		af.type		= sn;
		af.duration	= duration;
		af.bittype	= AFFECT_TO_CHAR;
		af.bitvector	= AFF2_FEAR;
		af.location	= APPLY_NONE;
		af.modifier	= mod;
		af.level     = level;
		affect_join( ch, vch, &af);
		
		vch->fear_level = mod;
	
		if (in_combat(vch))
		{
			do_withdraw(vch, NULL);
		}
		if (sn == gsn_cause_fear)
			break;
		if (sn == gsn_scare)
			count++;
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_chain_lightning)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	ROOM_INDEX_DATA *temp_room = victim->in_room;
	int dam, targets;
	int save;

	push_call("spell_chain_lightning(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act( "{178}You launch forth a bolt of lightning at $N!", ch, NULL, victim, TO_CHAR );
	act( "{178}$n launches forth a bolt of lightning at $N!", ch, NULL, victim, TO_NOTVICT );
	act( "{178}$n launches forth a bolt of lightning at you!", ch, NULL, victim, TO_VICT );
	
	targets = UMIN(level, 15);

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn) || !is_same_group(victim, vch))
		{
			continue;
		}

		dam = spell_dice(ch, sn, targets, 6);
		
		act( "{178}The bolt of chain lightning arcs to you.", vch, NULL, NULL, TO_CHAR);
		act( "{178}The bolt of chain lightning arcs to $n.",  vch, NULL, NULL, TO_ROOM);

		if ((save = save_resist(ch, vch, sn, level)) != TRUE)
		{
			if (save == PARTIAL)
				dam /= 2;
	
			damage( ch, vch, dam, sn, NULL );
		}

		if (--targets <= 0)
		{
			break;
		}
	}

	send_to_room("{178}The searing bolt of chain lightning arcs to the ground and dissipates.\n\r", temp_room);
	pop_call();
	return TRUE;
}

DO_SPELL(spell_charm)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_charm(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(ch->in_room->room_flags, ROOM_SAFE))
	{
		send_to_char("Wards of peace prevent your spell here.\n\r", ch);
		pop_call();
		return FALSE;
	}

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

	if (is_affected(victim, sn))
	{
		send_to_char( "They are already affected.\n\r", ch );
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (is_safe(ch, victim)
	|| (sn == gsn_charm_plant && race_type(victim) != RTYPE_PLANT)
	|| (sn == gsn_charm_animal && !IS_ANIMAL(victim))
	|| (sn == gsn_charm_person && !IS_HUMANOID(victim)))
	{
		send_to_char( "They seem to be unaffected by your magic.\n\r", ch );
		pop_call();
		return TRUE;
	}

	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}
	
	withdraw_combat(victim);
	
// 	if (!IS_NPC(victim))
// 	{
// 		act( "You look at $n with a more favorable demeanor.", ch, NULL, victim, TO_VICT );
// 		act( "$N looks at you with a favorable demeanor.", ch, NULL, victim, TO_CHAR );
// 		act( "$N looks at $n with a favorable demeanor.",  ch, NULL, victim, TO_NOTVICT );
// 		do_help(victim, "charm person");
// 		pop_call();
// 		return TRUE;
// 	}
// 
	af.type      = sn;
	af.duration  = hr * level;
	af.location  = 0;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF2_CHARMED;
	af.level     = level;
	affect_to_char( ch, victim, &af );
	if (IS_NPC(victim))
		REMOVE_BIT(victim->act, ACT_AGGRESSIVE);

	act( "You look at $n with a more favorable demeanor.", ch, NULL, victim, TO_VICT );
	act( "$N looks at you with a favorable demeanor.", ch, NULL, victim, TO_CHAR );
	act( "$N looks at $n with a favorable demeanor.",  ch, NULL, victim, TO_NOTVICT );
	pop_call();
	return TRUE;
}

DO_SPELL(spell_chill_touch)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	int dam;

	push_call("spell_chill_touch(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	dam = spell_dice(ch, sn, 1, 6);

	if (can_backstab(ch, victim, sn) && number_percent() > get_apply(victim, APPLY_FORTIFICATION))
	{
		int bs_dice = ROUNDUP(multi_skill_level(ch, gsn_backstab) / 2);
		int bs_dam = 0;

		if (get_monk_style(ch) == STYLE_SLEEPING_TIGER && class_level(ch, CLASS_MONK) >= 12)
			bs_dice++;

		if (bs_dice > 0)
		{
			if (learned(ch, gsn_greater_sneak_attack))
			{
				bs_dam = dice(bs_dice, 8);
			}
			else
			{
				bs_dam = dice(bs_dice, 6);
			}
		}
		dam += bs_dam;
	}

	damage( ch, victim, dam, sn, NULL );

	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}

	af.type      = sn;
	af.duration  = -1;
	af.location  = APPLY_STR;
	af.modifier  = -1;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_join( ch, victim, &af );

	act( "{068}You feel weaker as $n chills you.", ch, NULL, victim, TO_VICT );
	act( "{068}$N looks weaker from your chilling touch.", ch, NULL, victim, TO_CHAR );
	act( "{068}$N looks weaker from $n's chilling touch.",  ch, NULL, victim, TO_NOTVICT );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_circle_of_death)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch, *gch_next;
	int count, lvl, roll;

	push_call("spell_circle_of_death(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	roll = dice(UMIN(level, 20), 4);

	for (count = lvl = 0 ; lvl <= 9 ; lvl++)
	{
		for (gch = victim->in_room->first_person ; gch ; gch = gch_next)
		{
			gch_next = gch->next_in_room;

			if (gch->level != lvl)
				continue;

			if (sn == gsn_circle_of_death && !CAN_CRITICAL(gch))
				continue;
			if (sn == gsn_undeath_to_death && !IS_UNDEAD(gch))
				continue;

			if (can_mass_cast(ch, gch, sn))
				continue;
				
			if (count + gch->level > roll)
			{
				act( "$N resists your spell.\n\r", ch, NULL, gch, TO_CHAR );
				continue;
			}
			
			if (save_resist(ch, gch, sn, level))
				continue;
	
			count += gch->level;
			damage(ch, gch, gch->hit + 11, sn, NULL);

			if (count >= roll)
				break;
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_cloudkill)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	ROOM_TIMER_DATA rtd;
	ROOM_INDEX_DATA *room;

	push_call("spell_cloudkill(%p,%p,%p,%p)",sn,level,ch,vo);

	if( victim->in_room == NULL )
	{
		pop_call();
		return FALSE;
	}

	room = victim->in_room;

	if(IS_SET(room->room_flags, ROOM_FOG))
	{
		send_to_char( "The room is already foggy.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if(IS_SET(room->room_flags, ROOM_SAFE))
	{
		send_to_char( "Wards of peace prevent you from casting that.\n\r", ch );
		pop_call();
		return TRUE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= turn * level;
	rtd.bitvector	= ROOM_FOG;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{128}A cloud of green noxious fog billows around you!", victim, NULL, NULL, TO_ALL );
	if (room != ch->in_room)
		act( "{128}You conjure up a cloud of toxic fog", ch, NULL, NULL, TO_CHAR );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_color_spray)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch;
	CHAR_DATA *vch_next;
	int dam1, dam2;

	push_call("spell_color_spray)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn))
			continue;
		
		if (!is_same_group(victim, vch))
			continue;
			
		if (save_resist(ch, victim, sn, level))
			continue;
				
		dam1 = spell_dice(ch, sn, 2, 4);
		dam2 = spell_dice(ch, sn, 1, 4);

		act( "{138}You are hit by a {118}da{128}zz{138}li{148}ng {158}ra{168}in{118}bo{128}w {138}of light!", vch, NULL, NULL, TO_CHAR );
		act( "A {118}da{128}zz{138}li{148}ng {158}ra{168}in{118}bo{128}w {138} of light hits $n.", vch, NULL, NULL, TO_ROOM );

		if (vch->level <= 2)
		{
			AFFECT_DATA af;

			af.type      = gsn_color_spray;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.level     = level;

			af.duration  = dam1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_UNCONSCIOUS;
			affect_join( ch, vch, &af );

			af.duration  = dam1+dam2;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_BLIND;
			affect_join( ch, vch, &af );

			af.duration  = dam1+dam2+1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_STUNNED;
			affect_join( ch, vch, &af );
		}
		else if (vch->level <= 4)
		{
			AFFECT_DATA af;

			af.type      = gsn_color_spray;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.level     = level;

			af.duration  = dam1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_BLIND;
			affect_join( ch, vch, &af );

			af.duration  = dam1+1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_STUNNED;
			affect_join( ch, vch, &af );
		}
		else
		{
			AFFECT_DATA af;

			af.type      = gsn_color_spray;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.level     = level;

			af.duration  = 1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_STUNNED;
			affect_join( ch, vch, &af );
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_command)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	OBJ_DATA *obj, *obj_next;
	char *command = NULL;
	int cnt;

	if (victim == ch)
	{
		send_to_char ("You obey your every whim!\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!is_string(target_name))
	{
		act("Demand $N to what?", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	switch(tolower(*target_name))
	{
		case 'd':
		case 'f':
		case 'k':
			break;
		default:
			send_to_char("You can only order to 'drop', 'flee', or 'kneel'.\n\r", ch);
			pop_call();
			return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	sprintf(command, "%s'%s'", get_color_string(ch, COLOR_SPEECH, VT102_DIM), capitalize(target_name));
	act ("$n commands you, $t.", ch, command, victim, TO_VICT);
	act ("You command $N, $t.", ch, command, victim, TO_CHAR);
	act ("$n commands $N, $t.", ch, command, victim, TO_NOTVICT);

	for (cnt = 0, vch = victim->in_room->first_person ; vch ; vch = vch_next)
	{
		vch_next = vch->next;
		
		if (cnt && cnt >= level)
			break;

		if (sn == gsn_command && vch != victim)
			continue;

		if (!is_same_group(vch, victim))
			continue;
	
		if (save_resist(ch, vch, sn, level))
			continue;
		
		switch(tolower(*target_name))
		{
			case 'd':
				for (obj = vch->first_carrying ; obj ; obj = obj_next)
				{
					obj_next = obj->next_content;
					
					if (WEAR_LOC(obj, WEAR_HOLD) || WEAR_LOC(obj, WEAR_WIELD) || WEAR_LOC(obj, WEAR_DUAL_WIELD))
					{
						if (!IS_SET(obj->extra_flags, ITEM_NOREMOVE))
						{
							unequip_char(vch, obj, TRUE);
							drop_obj(vch, obj, NULL, TRUE);
						}
					}
				}
				break;
			case 'f':
				if (in_combat(vch))
				{
					do_withdraw(vch, "");
				}
				break;
			case 'k':
				act("You kneel before $n!", ch, NULL, vch, TO_VICT);
				act("$N kneels on the spot!", ch, NULL, vch, TO_VICT);
				update_pos(vch, POS_KNEELING);
				break;
			default:
				break;
		}
		cnt++;
	}

	pop_call();
	return TRUE;
}


DO_SPELL(spell_cone_of_cold)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int diceroll;

	push_call("spell_cone_of_cold(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	diceroll = spell_dice(ch, sn, UMIN(level, 15), 6);

	spell_damage(ch, victim, diceroll, sn, level);

	pop_call();
	return TRUE;
}


char *  const   weather_flags [] =
{
	"wet",
	"dry",
	"hot",	
	"cold",
	"windy",
	"calm",
	"*"
};


DO_SPELL(spell_control_weather)
{
	int value, change;
	
	push_call("spell_control_weather(%p,%p,%p,%p)",sn,level,ch,vo);

	if ((value = get_flag(target_name, weather_flags)) == -1)
	{
		send_to_char ("Do you want the weather cold, hot, calm, windy, wet, or dry?\n\r", ch );
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	change = level;
	if (!has_domain(ch, DOMAIN_WEATHER) && !!has_domain(ch, DOMAIN_AIR) && !!has_domain(ch, DOMAIN_WATER))
		change /= 2;
	change = UMAX(1, change);
	
	switch (value)
	{
		case 0:
			ch->in_room->area->weather_info->change += change;
			break;
		case 1:
			ch->in_room->area->weather_info->change -= change;
			break;
		case 2:
			ch->in_room->area->weather_info->temperature += change;
			break;
		case 3:
			ch->in_room->area->weather_info->temperature -= change;
			break;
		case 4:
			ch->in_room->area->weather_info->wind_speed += change;
			break;
		case 5:
			ch->in_room->area->weather_info->wind_speed -= change;
			break;
	}
	
	do_mpareaecho( ch, format("The weather patterns shift suddenly.", target_name) );
	weather_area_update(ch->in_room->area);
	
	pop_call();
	return TRUE;
}

DO_SPELL(spell_create_food)
{
	OBJ_DATA *mushroom;
	CHAR_DATA *gch;
	int cnt;

	push_call("spell_create_food(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	for (cnt = 0, gch = ch->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (!is_same_group(gch, ch))
		{
			continue;
		}
		if (cnt >= 3 * UMIN(level, 20))
		{
			continue;
		}
		mushroom = create_object( get_obj_index( OBJ_VNUM_MUSHROOM ), 0 );
		mushroom->value[0] = 24;
		obj_to_room( mushroom, ch->in_room->vnum );
	}
	act( "Food suddenly appears in front of $n.", ch, mushroom, NULL, TO_ROOM );
	act( "Food suddenly appears in front of you.", ch, mushroom, NULL, TO_CHAR );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_create_water)
{
	OBJ_DATA *obj = ( OBJ_DATA * ) vo;
	int water;

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

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

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	water = UMIN( level * 2, obj->value[0] - obj->value[1] );

	if ( water > 0 )
	{
		obj->value[2] = LIQ_WATER;
		obj->value[1] += water;
		act( "$p {168}is filled with clear water.{300}", ch, obj, NULL, TO_CHAR );
	}

	pop_call();
	return TRUE;
}


DO_SPELL(spell_create_spring)
{
	OBJ_DATA *spring;

	push_call("spell_create_spring(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	spring = create_object( get_obj_index( OBJ_VNUM_SPRING ), 0 );
	spring->timer = level;
	act( "{148}Tracing a ring before you, the graceful flow of a mystical spring emerges.", ch, spring, NULL, TO_CHAR );
	act( "{148}As $n traces a ring through the air, a mystical spring emerges.", ch, spring, NULL, TO_ROOM );
	obj_to_room( spring, ch->in_room->vnum );

	pop_call();
	return TRUE;
}

/*
 * The goal was to make this spell as fearful and nasty
 * as the original 3.0 spell, without making it a no-save
 * instadeath spell - Kregor 2/15/11
 */
DO_SPELL(spell_creeping_doom)
{
	ROOM_TIMER_DATA rtd;

	push_call("spell_creeping_doom(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(ch->in_room->room_flags, ROOM_SWARM))
	{
		act("This area is already infested.", ch, NULL, NULL, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (IS_SET(ch->in_room->room_flags, ROOM_SAFE))
	{
		act("The swarms refuse your call.", ch, NULL, NULL, TO_CHAR );
		pop_call();
		return TRUE;
	}

	rtd.vnum			= ch->in_room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= (spell_dice(ch, sn, 1, 6) + 4) * 100;
	rtd.duration	= turn * level;
	rtd.bitvector	= ROOM_SWARM;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{038}A swarm of vermin pour out from behind $n!", ch, NULL, NULL, TO_ROOM);
	act( "{038}A swarm of vermin pour out from behind you!", ch, NULL, NULL, TO_CHAR);

	pop_call();
	return TRUE;
}

/* 
 * unified cure and inflict spells into single 
 * functions for differing strengths - Kregor
 */
DO_SPELL(spell_cure)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int heal;

	push_call("spell_cure(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (sn == gsn_cure_light)
		heal = spell_dice(ch, sn, 1, 8) + UMIN(level, 5);
	else if (sn == gsn_cure_moderate)
		heal = spell_dice(ch, sn, 2, 8) + UMIN(level, 10);
	else if (sn == gsn_cure_serious)
		heal = spell_dice(ch, sn, 3, 8) + UMIN(level, 15);
	else if (sn == gsn_cure_critical)
		heal = spell_dice(ch, sn, 4, 8) + UMIN(level, 20);
	
	if (is_affected(victim, gsn_festering_wounds))
		heal /= 2;

	if (IS_UNDEAD(victim))
	{
		spell_damage( ch, victim, heal, sn, level );
	}
	else
	{
		victim->hit = UMIN( victim->hit + heal, get_max_hit(victim));
		victim->nonlethal = UMAX(0, victim->nonlethal - heal);
		update_pos(victim,-1);

		if (heal)
		{
			act( "{138}Your $t wounds mend and your pain ebbs.", victim, skill_table[sn].msg_off, NULL, TO_CHAR);
			act( "{138}$n's $t wounds mend and $s pain ebbs.",  victim, skill_table[sn].msg_off, NULL, TO_ROOM);
		}
		affect_strip(victim, gsn_bleed_damage);

	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_inflict)
{
	int dam;
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	
	push_call("spell_inflict(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (sn == gsn_inflict_light)
		dam = spell_dice(ch, sn, 1,8) + UMIN(level,5);
	else if (sn == gsn_inflict_minor)
		dam = spell_dice(ch, sn, 1, 3);
	else if (sn == gsn_inflict_moderate)
		dam = spell_dice(ch, sn, 2,8) + UMIN(level,10);
	else if (sn == gsn_inflict_serious)
		dam = spell_dice(ch, sn, 3,8) + UMIN(level,15);
	else if (sn == gsn_inflict_critical)
		dam = spell_dice(ch, sn, 4,8) + UMIN(level,20);
	
	if (IS_UNDEAD(victim))
	{
		victim->hit = UMIN( victim->hit + dam, get_max_hit(victim));
		update_pos(victim,-1);

		act( "{138}Your $t wounds mend and your pain ebbs.", victim, skill_table[sn].msg_off, NULL, TO_CHAR);
		act( "{138}$n's $t wounds mend.",  victim, skill_table[sn].msg_off, NULL, TO_ROOM);
	}
	else
	{
		spell_damage( ch, victim, dam, sn, level );
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_stabilize)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;

	push_call("spell_stabilize(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (IS_UNDEAD(victim))
	{
		act( "Your spell has no effect on $N.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return TRUE;
	}
	else
	{
		if (victim->position >= POS_STUNNED)
		{
			act( "{138}$n is not mortally wounded.",  ch, NULL, victim, TO_CHAR);
			pop_call();
			return TRUE;
		}
			
		victim->hit = 0;
		update_pos(victim,-1);
		affect_strip(victim,gsn_bleed_damage);

		act( "{138}You no longer feel your life slipping away.", victim, NULL, NULL, TO_CHAR);
		act( "{138}$n is no longer slipping into oblivion.",  victim, NULL, NULL, TO_ROOM);
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_mass_cure)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch;
	int heal, cure, cnt;

	push_call("spell_mass_cure_light(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (sn == gsn_mass_cure_light)
		heal = spell_dice(ch, sn, 1, 8) + UMIN(level, 25);
	else if (sn == gsn_mass_cure_moderate)
		heal = spell_dice(ch, sn, 2, 8) + UMIN(level, 30);
	if (sn == gsn_mass_cure_serious)
		heal = spell_dice(ch, sn, 3, 8) + UMIN(level, 35);
	if (sn == gsn_mass_cure_critical)
		heal = spell_dice(ch, sn, 4, 8) + UMIN(level, 40);

	for (cnt = 0, gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		cure = heal;

		if (cnt >= level)
		{
			continue;
		}
		if (!is_same_group(victim, gch))
			continue;

		if (IS_UNDEAD(gch))
		{
			if (!can_mass_cast(ch, gch, sn))
				continue;

			spell_damage( ch, gch, cure, sn, level );
			cnt++;
			continue;
		}

		if (is_affected(gch, gsn_festering_wounds))
			cure /= 2;

		gch->hit = UMIN( gch->hit + cure, get_max_hit(gch));
		gch->nonlethal = UMAX(0, gch->nonlethal - cure);
		affect_strip(gch,gsn_bleed_damage);
		update_pos(gch,-1);

		act( "{138}Your $t wounds mend and your pain ebbs.", gch, skill_table[sn].msg_off, NULL, TO_CHAR);
		act( "{138}$n's $t wounds mend and $s pain ebbs.",  gch, skill_table[sn].msg_off, NULL, TO_ROOM);
		cnt++;
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_mass_inflict)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch;
	int heal, cnt;

	push_call("spell_mass_inflict_light(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (sn == gsn_mass_inflict_light)
		heal = spell_dice(ch, sn, 1, 8) + UMIN(level, 25);
	else if (sn == gsn_mass_inflict_moderate)
		heal = spell_dice(ch, sn, 2, 8) + UMIN(level, 30);
	if (sn == gsn_mass_inflict_serious)
		heal = spell_dice(ch, sn, 3, 8) + UMIN(level, 35);
	if (sn == gsn_mass_inflict_critical)
		heal = spell_dice(ch, sn, 4, 8) + UMIN(level, 40);

	for (cnt = 0, gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (cnt >= level)
		{
			continue;
		}
		if (!IS_UNDEAD(gch))
		{
			if (!can_mass_cast(ch, gch, sn))
				continue;

			spell_damage( ch, gch, heal, sn, level );
			cnt++;
			continue;
		}

		gch->hit = UMIN( gch->hit + heal, get_max_hit(gch));
		gch->nonlethal = UMAX(0, gch->nonlethal - heal);
		affect_strip(gch,gsn_bleed_damage);
		update_pos(gch,-1);

		act( "{138}Your $t wounds mend and your pain ebbs.", gch, skill_table[sn].msg_off, NULL, TO_CHAR);
		act( "{138}$n's $t wounds mend.",  gch, skill_table[sn].msg_off, NULL, TO_ROOM);
		cnt++;
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_dancing_lights)
{
	OBJ_DATA *light;
	int cnt;

	push_call("spell_dancing_lights(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	for (cnt = 0 ; cnt < level / 3 ; cnt++)
	{
		if (cnt >= 4)
			break;

		light = create_object( get_obj_index( OBJ_VNUM_LIGHT_BALL ), 0 );
		light->timer = turn * level;
		act( "Shards of iridescent light collide to form a dazzling ball.", ch, NULL, NULL, TO_CHAR );
		act( "Shards of iridescent light collide to form a dazzling ball.", ch, NULL, NULL, TO_ROOM );
		obj_to_char( light, ch );

		cnt++;
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_darkness)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	ROOM_TIMER_DATA rtd;

	push_call("spell_darkness(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(ch->in_room->room_flags, ROOM_SAFE) || IS_SET(victim->in_room->room_flags, ROOM_SAFE))
	{
		act( "Wards prevent you from casting that here.", ch, NULL, NULL, TO_CHAR );
		pop_call();
		return FALSE;
	}
	if (IS_SET(victim->in_room->room_flags, ROOM_DARK))
	{
		act("The room is already dark.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	rtd.vnum			= victim->in_room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_ROOM_LIGHT;
	rtd.modifier	= -1;
	rtd.duration	= level * turn;
	rtd.bitvector	= ROOM_DARK;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);
	act( "{108}A cloud of shadows fills the area.", victim, NULL, NULL, TO_ALL);
	if (ch->in_room != victim->in_room)
		act( "{108}A cloud of shadows fills the area around $N.", ch, NULL, victim, TO_CHAR);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_daylight)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;
	ROOM_TIMER_DATA rtd;

	push_call("spell_daylight(%p,%p,%p,%p)",sn,level,ch,vo);

	if (target == TAR_OBJ_INV)
	{
		if (IS_OBJ_STAT(obj, ITEM_GLOW))
		{
			send_to_char( "That object already glows.\n\r", ch);
			pop_call();
			return FALSE;
		}	
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
	
		af.type				= sn;
		af.duration		= 5 * turn * level;
		af.bittype		= AFFECT_TO_OBJ;
		af.bitvector	= ITEM_GLOW;
		af.location		= APPLY_ROOM_LIGHT;
		af.modifier		= 2;
		af.level    	= level;
		affect_to_obj( ch,obj, &af);
	
		act( "$p {138}begins to glow with a bright light.", ch, obj, NULL, TO_CHAR);
		act( "$p {138}begins to glow with a bright light.", ch, obj, NULL, TO_ROOM);
	}
	else if (target == TAR_CHAR_DEFENSIVE)
	{
		if (is_room_affected(victim->in_room, gsn_blacklight)
		|| is_room_affected(victim->in_room, gsn_darkness))
		{
			if (!ForReal)
			{
				pop_call();
				return TRUE;
			}	
			affect_room_strip(victim->in_room, gsn_blacklight);
			affect_room_strip(victim->in_room, gsn_darkness);
		}
		else
		{
			if (IS_SET(victim->in_room->room_flags, ROOM_DAYLIGHT))
			{
				act("The room is already bright.", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return FALSE;
			}
			if (!ForReal)
			{
				pop_call();
				return TRUE;
			}	

			rtd.vnum			= ch->in_room->vnum;
			rtd.type			= sn;
			rtd.location	= APPLY_NONE;
			rtd.modifier	= 0;
			rtd.duration	= 5 * turn * level;
			rtd.bitvector	= ROOM_DAYLIGHT;
			rtd.level			= level;
			
			set_room_timer(ch, &rtd);
			act( "{138}The light of a noonday sun fills the area.", ch, NULL, NULL, TO_ALL);
			if (ch->in_room != victim->in_room)
				act( "138}The light of a noonday sun fills the area.", ch, NULL, NULL, TO_CHAR);
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_demand)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	char command[MAX_INPUT_LENGTH];
	int cmd;

	if (victim == ch)
	{
		send_to_char ("You obey your every whim!\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!is_string(target_name))
	{
		act("Demand $N to what?", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (IS_AFFECTED(victim, AFF_DOMINATE))
	{
		act("$N is already mind controlled.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return TRUE;
	}

	one_argument(target_name, command);
	
	SET_BIT(victim->affected_by, AFF_DOMINATE);

	if ((cmd = find_command(command, get_trust(victim))) == -1)
	{
		act ("$N can't do that!", ch, NULL, victim, TO_CHAR);
		REMOVE_BIT(victim->affected_by, AFF_DOMINATE);
		pop_call();
		return FALSE;
	}
	
	if (save_resist(ch, victim, sn, level))
	{
		REMOVE_BIT(victim->affected_by, AFF_DOMINATE);
		pop_call();
		return TRUE;
	}

	sprintf(command, "%s'%s'", get_color_string(ch, COLOR_SPEECH, VT102_DIM), capitalize(cmd_table[cmd].name));
	act ("$n commands you, $t.", ch, command, victim, TO_VICT);
	act ("You command $N, $t.", ch, command, victim, TO_CHAR);
	act ("$n commands $N, $t.", ch, command, victim, TO_NOTVICT);

	interpret (victim, target_name);
	REMOVE_BIT(victim->affected_by, AFF_DOMINATE);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_detect_alignment)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *rch;
	AFFECT_DATA af;

	push_call("spell_detect_alignment(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	af.type      = sn;
	af.duration  = level;
	af.modifier  = 0;
	af.location  = APPLY_NONE;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_join( ch, victim, &af );

	if (sn == gsn_detect_chaos)
		send_to_char_color( "{058}You attune yourself to entropic auras.\n\r", victim );
	if (sn == gsn_detect_evil)
		send_to_char_color( "{018}You attune yourself to unholy auras.\n\r", victim );
	if (sn == gsn_detect_good)
		send_to_char_color( "{138}You attune yourself to holy auras.\n\r", victim );
	if (sn == gsn_detect_law)
		send_to_char_color( "{178}You attune yourself to axiomatic auras.\n\r", victim );
		
	for (rch = victim->in_room->first_person ; rch ; rch = rch->next)
	{
		if (rch == victim)
			continue;
		if (sn == gsn_detect_chaos && IS_CHAOTIC(rch))
		{
			send_to_char_color( "{058}You sense a chaotic presence nearby!\n\r", victim );
			break;
		}
		if (sn == gsn_detect_evil && IS_EVIL(rch))
		{
			send_to_char_color( "{018}You sense an evil presence nearby!\n\r", victim );
			break;
		}
		if (sn == gsn_detect_good && IS_GOOD(rch))
		{
			send_to_char_color( "{138}You sense a goodly presence nearby!\n\r", victim );
			break;
		}
		if (sn == gsn_detect_law && IS_LAWFUL(rch))
		{
			send_to_char_color( "{178}You sense a lawful presence nearby!\n\r", victim );
			break;
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_detect_poison)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	bool poisoned = FALSE;

	push_call("spell_detect_poison(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (target == TAR_CHAR_DEFENSIVE)
	{
		if ( is_affected( victim, gsn_poison ) )
		{
			poisoned = TRUE;
		}
		if (victim->poison != NULL)
			poisoned = TRUE;
	
		if ( poisoned )
		{
			act( "{128}You can detect poison in $N's veins.", ch, NULL, victim, TO_CHAR );
		}
		else
		{
			act( "$N does not seem to be poisoned.", ch, NULL, victim, TO_CHAR );
		}
	}
	else 
	{
		if (obj->poison != NULL)
		{
			act( "{128}You detect poison on $p.", ch, obj, NULL, TO_CHAR );
		}
		else if (obj->item_type == ITEM_DRINK_CON || obj->item_type == ITEM_FOOD )
		{
			if ( obj->value[3] != 0 )
			{
				act( "{128}You detect poison on $p.", ch, obj, NULL, TO_CHAR );
			}
			else
			{
				act( "{138}$p doesn't look poisoned.", ch, obj, NULL, TO_CHAR );
			}
		}
		else
		{
			act( "{138}It doesn't look poisoned.", ch, NULL, NULL, TO_CHAR );
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_detect_shapechanger)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	bool Found = FALSE;

	push_call("spell_detect_shapechanger(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	af.type      = sn;
	af.duration  = turn * level;
	af.modifier  = 0;
	af.location  = APPLY_NONE;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_join( ch, victim, &af );

	send_to_char_color( "{148}You attune your senses to find altered forms.\n\r", victim );

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;
		
		if (IS_AFFECTED(vch, AFF_NONDETECTION))
			continue;
		
		if (is_polymorph(vch)
		|| rspec_req(vch, RSPEC_SHAPECHANGER))
		{
			send_to_char_color( "{108}You sense altered forms!\n\r", victim );
			Found = TRUE;
			break;
		}
	}
	
	if (!Found)
		send_to_char_color( "{078}You do not sense any presence here.\n\r", victim );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_detect_undead)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	bool Found = FALSE;

	push_call("spell_detect_undead(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	af.type      = sn;
	af.duration  = turn * level;
	af.modifier  = 0;
	af.location  = APPLY_NONE;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_join( ch, victim, &af );

	send_to_char_color( "{148}You attune your sight to the realm of undead.\n\r", victim );

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;
		
		if (IS_UNDEAD(vch) && !IS_AFFECTED(vch, AFF_NONDETECTION))
		{
			send_to_char_color( "{108}You sense the presence of undead nearby!\n\r", victim );
			Found = TRUE;
			break;
		}
	}
	
	if (!Found)
		send_to_char_color( "{078}You do not sense an undead presence.\n\r", victim );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_discern_location)
{
	CHAR_DATA *victim;
	char txt[MAX_STRING_LENGTH];

	push_call("spell_discern_location(%p,%p,%p,%p)",sn,level,ch,vo);

	if (*target_name == '\0')
	{
		send_to_char( "Who are you trying to locate?\n\r", ch );
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	*txt = '\0';

	for (victim = mud->f_char ; victim ; victim = victim->next)
	{
		if (!can_see_world(ch, victim))
		{
			continue;
		}
		
		if (IS_GOD(victim))
		{
			continue;
		}
		
		if (!IS_NPC(victim) && !knows_char(ch, victim))
		{
			continue;
		}

		if (!is_multi_name_list_short(target_name, PERS(victim, ch)))
		{
			if (!is_multi_name_list_short(target_name, adjective(victim)))
			{
				continue;
			}
		}
		
		if (!victim->in_room)
		{
			continue;
		}
		
		if (check_nondetection(ch, victim, gsn_discern_location))
		{
			continue;
		}
		
		cat_sprintf(txt, "%s is at %s in %s.\n\r", PERS(victim, ch), victim->in_room->name, victim->in_room->area->name);
		break;
	}

	if (*txt == '\0')
	{
		send_to_char( "You find no one like that in hell, earth, or heaven.\n\r", ch );
	}
	else
	{
		send_to_char( txt, ch );
	}

	pop_call();
	return TRUE;
}


DO_SPELL(spell_disjunction)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	OBJ_DATA *obj;
	AFFECT_DATA *paf, *paf_next;
	int paf_type;

	push_call("spell_disjunction(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{168}$n radiates a burst of antimagic energy.", ch, NULL, NULL, TO_ROOM);
	act( "{168}You radiate a burst of antimagic energy.", ch, NULL, NULL, TO_CHAR);

	for (vch = victim->in_room->first_person ; vch != NULL ; vch = vch_next)
	{
		vch_next = vch->next_in_room;

		if (vch == ch)
			continue;

		for ( paf_type = 0, paf = vch->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;

			if (!is_spell(paf->type))
				continue;
				
			// for permanency affects, make a caster level check - Kregor
			if (paf->duration >= 0)
			{
				if (skill_table[paf->type].msg_off)
				{
					act( skill_table[paf->type].msg_off, vch, NULL, NULL, TO_CHAR);
				}
				if (skill_table[paf->type].msg_off_room)
				{
					act( skill_table[paf->type].msg_off_room, vch, NULL, NULL, TO_ROOM);
				}
			}
			else if (dice(1,20) + level < 11 + paf->level)
			{
				continue;
			}
			paf_type = paf->type;
			affect_from_char(vch, paf);
		}
		for (obj = vch->first_carrying ; obj ; obj = obj->next_content)
		{
			for ( paf = obj->first_affect; paf != NULL; paf = paf_next )
			{
				paf_next = paf->next;
	
				if (!is_spell(paf->type))
				{
					continue;
				}
	
				if (paf->duration >= 0)
				{
					if (skill_table[paf->type].msg_obj_off)
					{
						act( skill_table[paf->type].msg_obj_off, vch, obj, NULL, TO_CHAR);
						if (vch->in_room)
							act( skill_table[paf->type].msg_obj_off, vch, obj, NULL, TO_ROOM);
					}
				}
				paf_type = paf->type;
				affect_from_obj(obj, paf);
			}
		}
	}
	if (paf_type == 0)
	{
		send_to_char( "Nothing appears to happen.\n\r", ch );
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_dispel_alignment)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_dispel_alignment(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if ((sn == gsn_dispel_evil && !IS_EVIL(victim))
	|| (sn == gsn_dispel_good && !IS_GOOD(victim))
	|| (sn == gsn_dispel_chaos && !IS_CHAOTIC(victim))
	|| (sn == gsn_dispel_law && !IS_LAWFUL(victim)))
	{
		act( "$N does not seem to be affected.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return TRUE;
	}

	//one spell where that numeric align scale matters! - Kregor
	if (sn == gsn_dispel_good || sn == gsn_dispel_evil)
		dam = abs(victim->alignment) / 10;
	else
		dam = abs(victim->ethos) / 10;

	spell_damage( ch, victim, dam, sn, level );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_dispel_magic)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	int paf_type, diceroll, count;

	push_call("spell_dispel_magic(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (arcane_mastery(ch, SCHOOL_ABJURATION))
		diceroll = UMAX(dice(1,20), dice(1,20)) + level;
	else
		diceroll = dice(1, 20) + level;
	count = 0;
	
	diceroll += learned(ch, gsn_focus_abj);
	
	if (target == TAR_CHAR_DEFENSIVE)
	{
		if (IS_ACT(victim, ACT_SUMMONED))
		{
			if (victim->master && arcane_mastery(victim->master, SCHOOL_CONJURATION))
				diceroll -= 4;

			if (!will_save(victim, ch, diceroll, sn))
			{
				act("$n is returned from where $e was summoned!", victim, NULL, NULL, TO_ROOM);
				act("You are returned from where you were summoned!", victim, NULL, NULL, TO_CHAR);
				pop_call();
				return TRUE;
			}
		}
		for ( paf_type = 0, paf = victim->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;

			if (!is_spell(paf->type))
				continue;
				
			if (sn == gsn_dispel_magic && IS_SET(paf->bitvector, AFF_CURSE))
				continue;
			
			if (!is_name(paf->caster, ch->name))
			{
				if (diceroll < 11 + paf->level)
				{
					continue;
				}
			}
			
			if (paf->duration >= 0)
			{
				if (skill_table[paf->type].msg_off)
				{
					act( skill_table[paf->type].msg_off, victim, NULL, NULL, TO_CHAR);
				}
				if (skill_table[paf->type].msg_off_room)
				{
					act( skill_table[paf->type].msg_off_room, victim, NULL, NULL, TO_ROOM);
				}
			}
			paf_type = paf->type;
			affect_from_char(victim, paf);
			
			if (sn == gsn_dispel_magic)
				break;
			
			count++;
			if (count >= level/4)
				break;
		}
		if (paf_type == 0)
		{
			send_to_char( "Nothing appears to happen.\n\r", ch );
		}
	}
	else
	{
		for ( paf_type = 0, paf = obj->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;

			if (!is_spell(paf->type))
			{
				continue;
			}

			if (!is_name(paf->caster, ch->name))
			{
				if (diceroll < 11 + paf->level)
				{
					continue;
				}
			}
			
			if (paf_type == 0 && paf->duration >= 0)
			{
				if (skill_table[paf->type].msg_obj_off)
				{
					act( skill_table[paf->type].msg_obj_off, ch, obj, NULL, TO_CHAR);
					if (ch->in_room)
						act( skill_table[paf->type].msg_obj_off, ch, obj, NULL, TO_ROOM);
				}
			}
			paf_type = paf->type;
			affect_from_obj(obj, paf);

			if (sn == gsn_dispel_magic)
				break;
			
			count++;
			if (count >= level/4)
				break;
		}
		if (paf_type == 0)
		{
			send_to_char( "Nothing appears to happen.\n\r", ch );
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_disrupt_undead)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_disrupt_undead(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	dam = spell_dice(ch, sn, 1, 6);

	if (!IS_UNDEAD(victim))
	{
		act( "$N is not affected by your spell.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return TRUE;
	}
	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}

	damage( ch, victim, dam, sn, NULL );

	pop_call();
	return TRUE;
}

/* USED to be charm person, but is more fitting for dominate - Kregor. */
DO_SPELL(spell_dominate)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_dominate_person(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(ch->in_room->room_flags, ROOM_SAFE))
	{
		send_to_char("Wards of peace prevent your spell here.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (victim == ch)
	{
		pop_call();
		return FALSE;
	}

	if (is_safe(ch, victim))
	{
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if ((sn == gsn_dominate_person && !IS_HUMANOID(victim))
	|| (sn == gsn_dominate_animal && !IS_ANIMAL(victim))
	|| domain_apotheosis(victim, DOMAIN_LIBERATION))
	{
		act( "$N seems to be unaffected by your magic.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return TRUE;
	}
	if (IS_AFFECTED(victim, AFF_DOMINATE))
	{
		send_to_char( "They are already dominated.\n\r", ch );
		pop_call();
		return FALSE;
	}
	
	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}
	
	withdraw_combat(victim);
	
	if ( victim->master )
	{
		stop_follower( victim );
	}
	add_follower( victim, ch );

	af.type      = sn;
	af.duration  = hr * level;
	af.location  = 0;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF_DOMINATE;
	af.level     = level;
	affect_to_char( ch, victim, &af );

	act( "Isn't $n just so nice?", ch, NULL, victim, TO_VICT );
	act( "$N turns to ragard you as $S master.", ch, NULL, victim, TO_CHAR );
	act( "$N turns to ragard $n as $S master.",  ch, NULL, victim, TO_NOTVICT );
	
	if (who_fighting(ch) && IS_NPC(who_fighting(ch)))
	{
		fight(victim, who_fighting(ch));
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_earthquake)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	EXIT_DATA *pExit;
	ROOM_TIMER_DATA rtd;
	int door, temp_vnum, vict_vnum, dam;

	push_call("spell_earthquake(%p,%p,%p,%p)",sn,level,ch,vo);

	/*
	 * no earthquakes in the air
	 */
	if (ch->in_room->sector_type == SECT_AIR)
	{
		send_to_char("You cannot cast this in the air.\n\r",ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "The earth trembles beneath $N's feet!", ch, NULL, victim, TO_CHAR );
	act( "The earth trembles beneath your feet!", victim, NULL, NULL, TO_CHAR );
	act( "$n makes the earth tremble and shiver.", ch, NULL, NULL, TO_ROOM );
	if (ch->in_room != victim->in_room)
		act( "The earth trembles beneath your feet!", victim, NULL, NULL, TO_ROOM );

	temp_vnum = ch->in_room->vnum;
	vict_vnum = victim->in_room->vnum;

	for (door = 0 ; door <= 5 ; door++)
	{
		if ((pExit = get_exit(vict_vnum, door)) != NULL && pExit->to_room != vict_vnum)
		{
			ch->in_room = room_index[pExit->to_room];
			act( "The earth trembles and shivers.", ch, NULL, NULL, TO_ROOM );
		}
	}
	ch->in_room = room_index[temp_vnum];
	
	switch (victim->in_room->sector_type)
	{
		case SECT_UNDER_GROUND:
		case SECT_DEEP_EARTH:
			if (!IS_SET(victim->in_room->room_flags, ROOM_BLOCK))
			{
				rtd.vnum			= victim->in_room->vnum;
				rtd.type			= sn;
				rtd.location	= APPLY_NONE;
				rtd.modifier	= -1;
				rtd.duration	= -1;
				rtd.bitvector	= ROOM_BLOCK;
				rtd.level			= level;
				
				set_room_timer(ch, &rtd);
			}
			act( "{108}The rocks cave in down around you!", victim, NULL, NULL, TO_ROOM);
			act( "{108}The rocks cave in around the area!", victim, NULL, NULL, TO_CHAR);
			break;
		case SECT_MOUNTAIN:
		case SECT_HILLS:
			act( "{108}The rocks around you crumble in a landslide!", victim, NULL, NULL, TO_ROOM);
			act( "{108}The rocks around you crumble in a landslide!", victim, NULL, NULL, TO_CHAR);
			break;
		case SECT_FIELD:
		case SECT_FOREST:
		case SECT_ROAD:
			act( "{178}Large fissures open up in the ground!", victim, NULL, NULL, TO_ROOM);
			act( "{178}Large fissures open up in the ground!", victim, NULL, NULL, TO_CHAR);
			break;
		case SECT_INSIDE:
			if (!IS_SET(victim->in_room->room_flags, ROOM_BLOCK))
			{
				rtd.vnum			= victim->in_room->vnum;
				rtd.type			= sn;
				rtd.location	= APPLY_NONE;
				rtd.modifier	= -1;
				rtd.duration	= -1;
				rtd.bitvector	= ROOM_BLOCK;
				rtd.level			= level;
				
				set_room_timer(ch, &rtd);
			}
			act( "{178}The ceiling above you collapses!", victim, NULL, NULL, TO_ROOM);
			act( "{178}The ceiling above you collapses!", victim, NULL, NULL, TO_CHAR);
			break;
		case SECT_CITY:
			act( "{178}Rubble crumbles from the buildings above you!", victim, NULL, NULL, TO_ROOM);
			act( "{178}Rubble crumbles from the buildings above you!", victim, NULL, NULL, TO_CHAR);
			break;
		case SECT_LAKE:
		case SECT_RIVER:
		case SECT_OCEAN:
			act( "{168}The water churns around you in waves!", victim, NULL, NULL, TO_ROOM);
			act( "{168}The water churns around you in waves!", victim, NULL, NULL, TO_CHAR);
			break;
	}

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (vch == ch)
			continue;

		if (!can_mass_cast(ch, vch, sn))
			continue;

		if (vch->in_room->sector_type == SECT_UNDER_GROUND
		|| vch->in_room->sector_type == SECT_DEEP_EARTH
		|| vch->in_room->sector_type == SECT_MOUNTAIN
		|| vch->in_room->sector_type == SECT_HILLS
		|| vch->in_room->sector_type == SECT_CITY
		|| vch->in_room->sector_type == SECT_INSIDE)
		{
			dam = spell_dice(ch, sn, 8, 6);
			if (!refl_save(vch, NULL, 15, -1))
			{
				damage(ch, vch, dam, sn, NULL);
				update_pos(vch, POS_RESTING);
			}
			else
			{
				damage(ch, vch, dam/2, sn, NULL);
			}
		}
		if (vch->in_room->sector_type == SECT_FIELD
		|| vch->in_room->sector_type == SECT_FOREST
		|| vch->in_room->sector_type == SECT_ROAD)
		{
			if (IS_FLYING(vch))
			{
				continue;
			}
			if (!refl_save(vch, NULL, 15, -1))
			{
				update_pos(vch, POS_RESTING);
			}
			if (number_percent() < 25)
			{
				act( "$N {118}falls into a fissure as it closes, crushing $M!", ch, NULL, vch, TO_NOTVICT);
				act( "$N {118}falls into a fissure as it closes, crushing $M!", ch, NULL, vch, TO_CHAR);
				act( "{118}You fall into a fissure as it closes, crushing you!", ch, NULL, vch, TO_VICT);
				if (!refl_save(vch, NULL, 20, -1))
					damage(ch, vch, 10 * level, sn, NULL);
				else
					damage(ch, vch, dice(3,6)+level, sn, NULL);
			}
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_electric_jolt)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_electric_jolt(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{178}A tiny jolt of energy shoots from $n's hand.", ch, NULL, NULL, TO_ROOM );
	act( "{178}A tiny jolt of energy shoots from your hand.", ch, NULL, NULL, TO_CHAR );

	dam  = spell_dice(ch, sn, 1,4) + UMIN(level/2, 5);

	spell_damage(ch, victim, dam, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_ray_of_frost)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_ray_of_frost(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{168}A tiny ray of cold shoots from $n's hand.", ch, NULL, NULL, TO_ROOM );
	act( "{168}A tiny ray of cold shoots from your hand.", ch, NULL, NULL, TO_CHAR );

	dam  = spell_dice(ch, sn, 1,4) + UMIN(level/2, 5);

	spell_damage(ch, victim, dam, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_flame_tongue)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_flame_tongue(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{118}A tiny blast of fire shoots from $n's finger.", ch, NULL, NULL, TO_ROOM );
	act( "{118}A tiny blast of fire shoots from your finger.", ch, NULL, NULL, TO_CHAR );

	dam  = spell_dice(ch, sn, 1,4) + UMIN(level/2, 5);

	spell_damage(ch, victim, dam, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_endure_elements)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	int resist;

	push_call("spell_endure_elements(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	resist = 5;

	af.type = sn;
	af.duration	 = UMIN(2 * level * hr, 24);
	af.bittype 	 = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;

	af.location	 = APPLY_DR_COLD;
	af.modifier	 = resist;
	affect_join( ch, victim, &af );

	af.location	 = APPLY_DR_FIRE;
	af.modifier	 = resist;
	affect_join( ch, victim, &af );

	act( "{138}A minor ward against the elements surrounds $n's body.", victim, NULL, NULL, TO_ROOM );
	act( "{138}A minor ward against the elements surrounds your body.", victim, NULL, NULL, TO_CHAR );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_enervation)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_enervation(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}

	af.type      = sn;
	af.duration  = UMIN(hr * level, 15);
	af.modifier  = 0 - dice(1, 4);
	af.location  = APPLY_LEVEL;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = AFF_NONE;
	af.level		 = level;
	affect_join( ch, victim, &af );

	act( "{108}$N wails as the life force is drawn from him!", victim, NULL, NULL, TO_ROOM);
	act( "{108}You wail as your life force is drawn from you!", victim, NULL, NULL, TO_CHAR);

	update_pos(victim,-1);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_energy_drain)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_energy_drain(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}

	af.type      = gsn_drain;
	af.duration  = -1;
	af.modifier  = 0 - dice(2, 4);
	af.location  = APPLY_LEVEL;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = AFF_NONE;
	af.level		 = level;
	affect_join( ch, victim, &af );

	act( "{108}$N wails as the life force is drawn from him!", victim, NULL, NULL, TO_ROOM);
	act( "{108}You wail as your life force is drawn from you!", victim, NULL, NULL, TO_CHAR);

	update_pos(victim,-1);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_entangle)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	ROOM_TIMER_DATA rtd;

	push_call("spell_entangle(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(victim->in_room->room_flags, ROOM_ENTANGLE))
	{
		send_to_char("This area is already affected.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (IS_SET(victim->in_room->room_flags, ROOM_SAFE))
	{
		send_to_char( "The plants here refuse to be beguiled.\n\r", ch );
		pop_call();
		return FALSE;
	}

	switch (victim->in_room->sector_type)
	{
		case SECT_FIELD:
		case SECT_FOREST:
		case SECT_HILLS:
		case SECT_MOUNTAIN:
		case SECT_SWAMP:
		case SECT_BEACH:
			break;
		default:
			send_to_char("That cannot be cast here.\n\r", ch);
			pop_call();
			return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	rtd.vnum			= victim->in_room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= turn * level;
	rtd.bitvector	= ROOM_ENTANGLE;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{128}Plant life begins to grow and flail around you!", victim, NULL, NULL, TO_CHAR);
	act( "{128}Plant life begins to grow and flail around you!", victim, NULL, NULL, TO_ROOM);

	if (ch->in_room != victim->in_room)
	{
		act( "{128}Plant life begins to grow and flail around $N!", ch, NULL, victim, TO_CHAR);
	}

	pop_call();
	return TRUE;
}

/*
 * Eyebite spell worth the effort to do it standalone
 * in order to do it right - Kregor
 */
DO_SPELL(spell_eyebite)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	int victsave, duration;

	push_call("spell_eyebite(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{118}$n gazes at $N with an evil eye!", ch, NULL, victim, TO_NOTVICT );
	act( "{118}$n gazes at you with an evil eye!", ch, NULL, NULL, TO_VICT );

	if (!CAN_CRITICAL(victim) || (victsave = save_resist(ch, victim, sn, level)) == TRUE)
	{
		act( "{118}$N is unaffected by your evil eye.", ch, NULL, NULL, TO_CHAR );
		pop_call();
		return TRUE;
	}	
	
	if (victsave == PARTIAL)
	{
		duration = dice(2,4);
	}
	else
	{
		duration = level * turn;
	}

	act( "{118}You gaze at $N with an evil eye!", ch, NULL, NULL, TO_CHAR );

	if (victim->level <= 4 && !is_immune(victim, SDESC_PARALYSIS))
	{
		af.type      = sn;
		af.duration  = duration;
		af.location  = 0;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_PARALYSIS;
		af.level     = level;
		affect_join( ch, victim, &af );
	}
	if ((victim->level >= 5 || victim->level <= 9) && !is_immune(victim, SDESC_FEAR))
	{
		af.type		= sn;
		af.duration	= duration;
		af.bittype	= AFFECT_TO_CHAR;
		af.bitvector	= AFF2_FEAR;
		af.location	= APPLY_NONE;
		af.modifier	= victsave ? 1 : 3;
		af.level     = level;
		affect_join( ch, victim, &af);
		
		victim->fear_level = af.modifier;
	
		if (!victsave)
			act( "You become filled with fear!", victim, NULL, NULL, TO_CHAR );
		else
			act( "You become shaken!", victim, NULL, NULL, TO_CHAR );

		if (in_combat(victim))
		{
			do_withdraw(victim, NULL);
		}
	}
	af.type      = sn;
	af.duration  = duration;
	af.location  = 0;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF2_SICKENED;
	af.level     = level;
	affect_join( ch, victim, &af );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_faerie_fire)
{
	CHAR_DATA *vch;
	AFFECT_DATA af;

	push_call("spell_faerie_fire(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	for ( vch = ch->in_room->first_person; vch != NULL; vch = vch->next_in_room )
	{
		if ( !IS_NPC(vch) && IS_SET(vch->act, PLR_WIZINVIS) )
			continue;
		if ( !IS_NPC(vch) && IS_SET(vch->act, PLR_WIZCLOAK) )
			continue;
		if( IS_NPC(vch) && IS_SET(vch->act, ACT_MOBINVIS) )
			continue;
		if (vch == ch)
			continue;
			
		if (save_resist(ch, vch, sn, level))
			continue;

		af.type      = sn;
		af.duration  = turn * level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;
		affect_join( ch, vch, &af );

		act( "{158}$n is surrounded by a pink outline.", vch, NULL, NULL, TO_ROOM );
		act( "{158}You are surrounded by a pink outline.", vch, NULL, NULL, TO_CHAR );
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_faithful_hound)
{
	ROOM_INDEX_DATA *room;
	ROOM_TIMER_DATA rtd;

	push_call("spell_faithful_hound(%p,%p,%p,%p)",sn,level,ch,vo);

	if (ch->in_room == NULL)
	{
		pop_call();
		return FALSE;
	}

	room = ch->in_room;

	if (is_room_affected(room, gsn_faithful_hound))
	{
		send_to_char( "There is already a spectral hound here.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (room->area->low_r_vnum == ROOM_VNUM_ARENA)
	{
		send_to_char("You may not conjure your hound here.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= hr * level;
	rtd.bitvector	= 0;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	send_to_char("{108}You conjure up an spectral watchdog to guard you.\n\r", ch);

	pop_call();
	return TRUE;
}

/*
 * Function for the Faith Hammer alignment spells:
 * Chaos Hammer, Holy Smite, Order's Wrath and Unholy Blight 
 * all pass thru this - Kregor
 */
DO_SPELL(spell_faith_hammer)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int vchsave, dam, dam1, dam2;
	bool ethos = FALSE;
	char name[20];

	push_call("faith_hammer(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (sn == gsn_unholy_blight)
	{
		act( "{108}$n unleashes a cold miasma of darkness!", ch, NULL, NULL, TO_ROOM );
		act( "{108}You unleash a cold miasma of darkness!", ch, NULL, NULL, TO_CHAR );
	}
	if (sn == gsn_orders_wrath)
	{
		act( "{138}$n unleashes a three-dimensional grid of energy!", ch, NULL, NULL, TO_ROOM );
		act( "{138}You unleash a three-dimensional grid of energy!", ch, NULL, NULL, TO_CHAR );
	}
	if (sn == gsn_holy_smite)
	{
		act( "{178}$n unleashes a purifying sphere of light!", ch, NULL, NULL, TO_ROOM );
		act( "{178}You unleash a purifying sphere of light!", ch, NULL, NULL, TO_CHAR );
	}
	if (sn == gsn_chaos_hammer)
	{
		act( "{158}$n unleashes an explosion of leaping energy bolts!", ch, NULL, NULL, TO_ROOM );
		act( "{158}You unleash an explosion of leaping energy bolts!", ch, NULL, NULL, TO_CHAR );
	}

	strcpy(name, skill_table[sn].name);
	
	dam1 = spell_dice(ch, sn, UMIN(level/2, 5), 8);
	dam2 = spell_dice(ch, sn, UMIN(level, 10), 8);

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;
		
		if (!can_mass_cast(ch, vch, sn))
			continue;

		if ((sn == gsn_unholy_blight && IS_EVIL(vch))
		|| (sn == gsn_holy_smite && IS_GOOD(vch))
		|| (sn == gsn_chaos_hammer && IS_CHAOTIC(vch))
		|| (sn == gsn_orders_wrath && IS_LAWFUL(vch)))
			continue;
			
		if (sn == gsn_orders_wrath && sn == gsn_chaos_hammer)
			ethos = TRUE;

		if (race_type(vch) == RTYPE_OUTSIDER)
			dam = dam2;
		else
			dam = dam1;
			
		if (ethos && IS_UNCONCERNED(vch))
			dam /= 2;
		else if (IS_NEUTRAL(vch))
			dam /= 2;

		if ((vchsave = save_resist(ch, vch, sn, level)) == TRUE)
		{
			continue;
		}
		else if (vchsave == PARTIAL)
		{
			damage(ch, vch, dam/2, sn, NULL);
			continue;
		}
		else
		{
			damage(ch, vch, dam, sn, NULL);
		}
		
		if (!valid_fight(ch, vch))
			continue;

		if ((ethos && IS_UNCONCERNED(vch)) || IS_NEUTRAL(vch))
			continue;

		af.type      = sn;
		af.duration  = dice(1,6);
		af.location  = 0;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		if (sn == gsn_unholy_blight)
			af.bitvector = AFF2_SICKENED;
		else if (sn == gsn_chaos_hammer)
			af.bitvector = AFF2_STAGGERED;
		else if (sn == gsn_orders_wrath)
			af.bitvector = AFF2_DAZED;
		else
			af.bitvector = AFF_BLIND;
		af.level     = level;
		affect_join( ch, vch, &af );
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_farheal)
{
	CHAR_DATA *victim;
	int heal;

	push_call("spell_farheal(%p,%p,%p,%p)",sn,level,ch,vo);

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

	if (victim->in_room == NULL
		||   IS_SET(victim->in_room->room_flags, ROOM_PRIVATE)
		||   IS_SET(victim->in_room->room_flags, ROOM_SOLITARY)
		||   victim->in_room->sector_type == SECT_ETHEREAL
		||   victim->in_room->sector_type == SECT_ASTRAL
		||   ch->in_room->sector_type == SECT_ETHEREAL
		||   ch->in_room->sector_type == SECT_ASTRAL
		||   IS_NPC(victim))
	{
		act("You can not reach $N through the void.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}
	if (victim == ch)
	{
		send_to_char("You can't farheal yourself!\n\r", ch);
		pop_call();
		return FALSE;
	}
	if (victim->in_room == ch->in_room)
	{
		ch_printf_color(ch, "%s is in the same room as you!\n\r",
		get_name(victim));
		pop_call();
		return FALSE;
	}
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	heal = UMIN(level * 10, 200);
	
	if (is_affected(ch, gsn_festering_wounds))
		heal /= 2;

	victim->hit = UMIN( victim->hit + heal, get_max_hit(victim) );
	update_pos(victim,-1);

	act( "{138}$n reaches through the void to fill you with healing energy.", ch, NULL, victim, TO_VICT );
	act( "{138}$n's wounds spontaneously heal!", victim, NULL, NULL, TO_ROOM );
	act ("{138}You reach through the void to $N, filling $M with healing energy.", ch, NULL, victim, TO_CHAR );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_fatigue)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch;
	int cnt;

	push_call("spell_fatigue(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	for (cnt = 0, vch = victim->in_room->first_person ; vch ; vch = vch->next_in_room)
	{
		if (sn == gsn_touch_of_fatigue && victim != vch)
			continue;

		if (!is_same_group(vch, victim))
			continue;

		if (cnt >= level)
			continue;

		if (save_resist(ch, victim, sn, level))
			continue;

		if (sn == gsn_waves_of_exhaustion)
		{
			vch->move = 0;
			act( "{108}$n suddenly looks exhausted.", vch, NULL, NULL, TO_ROOM );
			act( "{108}You feel totally exhausted.", vch, NULL, NULL, TO_CHAR );
		}
		else if (sn == gsn_waves_of_fatigue)
		{
			vch->move -= (vch->move / 3 * 2 + 1);
			act( "{108}$n suddenly looks fatigued.", vch, NULL, NULL, TO_ROOM );
			act( "{108}You feel fatigued.", vch, NULL, NULL, TO_CHAR );
		}
		else
		{
			vch->move -= (vch->move / 3 + 1);
			act( "{108}$n looks a bit less energetic.", vch, NULL, NULL, TO_ROOM );
			act( "{108}You feel more tired.", vch, NULL, NULL, TO_CHAR );
		}
		cnt++;
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_finger_of_death)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam = 0;
	int save;

	push_call("spell_finger_of_death(%p,%p,%p,%p)",sn,level,ch,vo);

	if (victim == ch)
	{
		send_to_char_color("You shouldn't point at yourself with that!", ch);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{118}You point a finger at $N...", ch, NULL, victim, TO_CHAR );
	act( "{118}$n points a finger at you...", ch, NULL, victim, TO_VICT );
	act( "{118}$n points a finger at $N...", ch, NULL, victim, TO_NOTVICT );		

	if ((save = save_resist(ch, victim, sn, level)) == -1)
	{
		dam = spell_dice(ch, sn, 3, 6) + level;
	}
	else if (!save)
	{
		dam = level * 10;
	}
	else
	{
		pop_call();
		return TRUE;
	}
	damage(ch, victim, dam, sn, NULL);
	pop_call();
	return TRUE;
}

DO_SPELL(spell_fireball)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int diceroll;

	push_call("spell_fireball(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	diceroll = spell_dice(ch, sn, UMIN(level, 10), 6);

	spell_damage(ch, victim, diceroll, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_firestorm)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	EXIT_DATA *pExit;
	int door, temp_vnum, dam;

	push_call("spell_firestorm(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{118}Sheets of fire and flame spew forth by your command!", ch, NULL, NULL, TO_CHAR);
	act( "{118}Sheets of fire and flame spew forth by $n's command.", ch, NULL, NULL, TO_ROOM);

	temp_vnum = ch->in_room->vnum;

	for (door = 0 ; door <= 5 ; door++)
	{
		if ((pExit = get_exit(temp_vnum, door)) && pExit->to_room != temp_vnum)
		{
			ch->in_room = room_index[pExit->to_room];
			act( "{118}The air about you sears with heat.", ch, NULL, NULL, TO_ROOM);
		}
	}
	ch->in_room = room_index[temp_vnum];
	
	dam = spell_dice(ch, sn, UMIN(level, 20), 6);

	spell_damage(ch, victim, dam, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_flame_arrow)
{
	OBJ_DATA *obj;
	AFFECT_DATA af;

	push_call("spell_flame_arrow(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
	{
		if (obj->item_type != ITEM_AMMO || IS_OBJ_STAT(obj, ITEM_MAGIC) || obj->first_affect != NULL)
			continue;

		if (IS_SET(obj->value[1], WFLAG_FLAMING))
			continue;

		act( "{118}Flames ignite from $p.", ch, obj, NULL, TO_CHAR);
		act( "{118}Flames ignite from $p.", ch, obj, NULL, TO_ROOM);

		af.type      = sn;
		af.duration  = turn * level;
		af.location  = APPLY_WEAPON_FLAG;
		af.modifier  = WFLAG_FLAMING;
		af.bittype   = AFFECT_TO_OBJ;
		af.bitvector = ITEM_MAGIC;
		af.level     = level;
		affect_to_obj( ch,obj, &af);
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_flame_blade)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_flame_blade(%p,%p,%p,%p)",sn,level,ch,vo);

	if (obj->item_type != ITEM_WEAPON)
	{
		act( "You can only imbue weapons with fire.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (IS_SET(obj->value[1], WFLAG_FLAMING))
	{
		act( "$p is already burning.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{118}Flames ignite from $p.", ch, obj, NULL, TO_CHAR);
	act( "{118}Flames ignite from $p.", ch, obj, NULL, TO_ROOM);

	af.type      = sn;
	af.duration  = turn*level;
	af.location  = APPLY_WEAPON_FLAG;
	af.modifier  = WFLAG_FLAMING;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_to_obj( ch,obj, &af);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_keen_edge)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_keen_edge(%p,%p,%p,%p)",sn,level,ch,vo);

	if (obj->item_type != ITEM_WEAPON)
	{
		act( "You can only give weapons a keen edge", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (IS_SET(obj->value[1], WFLAG_KEEN))
	{
		act( "$p is already keen.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	switch (weapon_table[obj->value[0]].dam_type)
	{
		case WEAPON_SLASH:
		case WEAPON_PIERCE:
		case WEAPON_PIERCE_SLASH:
			break;
		default:
			act( "$p is not an edged weapon.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
	}			

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{178}The edge on $p grows sharper!", ch, obj, NULL, TO_CHAR);

	af.type      = sn;
	af.duration  = turn*level;
	af.location  = APPLY_WEAPON_FLAG;
	af.modifier  = WFLAG_KEEN;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_to_obj( ch,obj, &af);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_flamestrike)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int diceroll;

	push_call("spell_flamestrike(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	diceroll = spell_dice(ch, sn, UMIN(level, 15), 6);

	act( "$n {118}calls down a column of divine fury and fire!", ch, NULL, NULL, TO_ROOM);
	act( "{118}You call down a column of divine fury and fire!", ch, NULL, NULL, TO_CHAR);

	spell_damage(ch, victim, diceroll, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_fly)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_fly(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if ( is_affected(victim, sn) )
	{
		act( "{168}$n's power of flight renews.", victim, NULL, NULL, TO_ROOM );
		act( "{168}Your power of flight renews.", victim, NULL, NULL, TO_CHAR );
	}
	else
	{
		act( "{168}Your feet rise off the ground.", victim, NULL, NULL, TO_CHAR );
		act( "{168}$n's feet rise off the ground.", victim, NULL, NULL, TO_ROOM );
	}

	af.type      = sn;
	af.duration  = turn * level;
	af.location  = 0;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF_FLYING;
	af.level     = level;
	affect_join( ch, victim, &af );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_floating_disc)
{
	AFFECT_DATA af;

	push_call("spell_floating_disc(%p,%p,%p,%p)",sn,level,ch,vo);

	if (is_affected(ch, sn))
	{
		act( "You can only have one floating disc at once.", ch, NULL, NULL, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	{
		act( "{158}A floating disc of force materializes before $n.", ch, NULL, NULL, TO_ROOM );
		act( "{158}A floating disc of force materializes before you.", ch, NULL, NULL, TO_CHAR );
	}

	af.type = sn;
	af.duration  = hr * level;
	af.modifier  = 0;
	af.location  = APPLY_NONE;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_join( ch, ch, &af );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_fog_cloud)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	ROOM_TIMER_DATA rtd;
	ROOM_INDEX_DATA *room;

	push_call("spell_fog_cloud(%p,%p,%p,%p)",sn,level,ch,vo);

	if( victim->in_room == NULL )
	{
		pop_call();
		return FALSE;
	}

	room = victim->in_room;

	if( IS_SET(room->room_flags, ROOM_FOG ) )
	{
		send_to_char( "The room is already foggy.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if( IS_SET(room->room_flags, ROOM_SAFE ) )
	{
		send_to_char("Wards of peace prevent you from doing that.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= turn * level;
	rtd.bitvector	= ROOM_FOG;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{108}A dark cloud of fog suddenly rolls in...", victim, NULL, NULL, TO_ALL );
	if (room != ch->in_room)
		act( "{108}You conjure up a dark cloud of fog!", ch, NULL, NULL, TO_CHAR );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_forceful_hand)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	EXIT_DATA *pExit;
	ROOM_INDEX_DATA *to_room;
	int check, dir;

	push_call("spell_forceful_hand(%p,%p,%p,%p)",sn,level,ch,vo);

	if (victim == ch)
	{
		send_to_char_color("You can't heavy hand yourself.\n\r", ch);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if ((check = combat_maneuver_check(ch, victim, sn)) >= 10)
	{
		act("A giant hand shoves you away from $n!", ch, NULL, victim, TO_VICT);
		act("A giant hand shoves $N away from $n!", ch, NULL, victim, TO_NOTVICT);
		act("A giant hand shoves $N away from you!", ch, NULL, victim, TO_CHAR);
		for (dir = 0 ; dir <= 5 ; dir++)
		{
			if ((pExit = get_exit(ch->in_room->vnum, dir)) == NULL
			|| (to_room = room_index[pExit->to_room]) == NULL)
			{
				continue;
			}
			if (can_move_char(ch, dir))
			{
				char_from_room(victim);
				char_to_room(victim, to_room->vnum, TRUE);
				act( "$N is shoved in from $T by a large hand!", ch, rev_dir_name[dir], victim, TO_NOTVICT);
				break;
			}
		}
		if (in_same_room(ch, victim))
		{
			damage(ch, victim, dice(1,8)+8, sn, NULL);
		}
		update_pos(victim, POS_RESTING);
	}			
	else if (check >= 0)
	{
		act("A giant hand shoves you away from $n!", ch, NULL, victim, TO_VICT);
		act("A giant hand shoves $N away from $n!", ch, NULL, victim, TO_NOTVICT);
		act("A giant hand shoves $N away from you!", ch, NULL, victim, TO_CHAR);
		victim->distracted = 2;
		update_pos(victim, POS_RESTING);
	}
	else
	{
		act("A giant hand tries to shove you away, and fails.", ch, NULL, victim, TO_VICT);
		act("A giant hand tries to shove $N, and fails.", ch, NULL, victim, TO_NOTVICT);
		act("A giant hand tries to shove $N, and fails.", ch, NULL, victim, TO_CHAR);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_gate)
{
	MOB_INDEX_DATA *pMob;
	CHAR_DATA *mh;
	bool fMatch;
	int cnt, count, hitdice, rdm, race;

	push_call("spell_gate(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(ch->in_room->room_flags, ROOM_SAFE))
	{
		send_to_char("Wards bar your summoning spell.\n\r", ch);
		pop_call();
		return FALSE;
	}
	
// 	if (ch->gold < 1000000)
// 	{
// 		act("You do not have enough gold to sacrifice for this spell.", ch, NULL, NULL, TO_CHAR);
// 		pop_call();
// 		return FALSE;
// 	}
// 
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (cnt = race = 0 ; race < MAX_RACE ; race++)
	{
		if (race_table[race].type != RTYPE_OUTSIDER)
			continue;
		if (!IS_SET(race_table[race].flags, RSPEC_GOOD) && IS_GOOD(ch))
			continue;
		if (!IS_SET(race_table[race].flags, RSPEC_EVIL) && IS_EVIL(ch))
			continue;
		if (race_table[race].hit_dice > level)
			continue;

		cnt++;
		fMatch = TRUE;
	}
	
	if (!fMatch)
	{
		act( "You can't seem to summon forth an ally.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	rdm = number_range(1, cnt);

	for (cnt = race = 0 ; race < MAX_RACE ; race++)
	{
		if (race_table[race].type != RTYPE_OUTSIDER)
			continue;
		if (!IS_SET(race_table[race].flags, RSPEC_GOOD) && IS_GOOD(ch))
			continue;
		if (!IS_SET(race_table[race].flags, RSPEC_EVIL) && IS_EVIL(ch))
			continue;
		if (race_table[race].hit_dice > level)
			continue;

		if (cnt++ == rdm)
			break;
	}
	
	hitdice = race_table[race].hit_dice;
	count   = level / hitdice; 

	pMob = get_mob_index(MOB_VNUM_SUMMONS);

	while (count)
	{
		mh = create_mobile(pMob);
		char_to_room( mh, ch->in_room->vnum, TRUE );
	
		mh->race				= race;
		mh->level				= race_table[race].hit_dice;
		mh->perm_str		= 10 + race_table[race].race_mod[0];
		mh->perm_dex		= 10 + race_table[race].race_mod[1];
		mh->perm_con		= 10 + race_table[race].race_mod[2];
		mh->perm_int		= 10 + race_table[race].race_mod[3];
		mh->perm_wis		= 10 + race_table[race].race_mod[4];
		mh->perm_cha		= 10 + race_table[race].race_mod[5];
		mh->size				= race_table[race].size;
		mh->height			= race_table[race].height;
		mh->weight			= race_table[race].weight;
		mh->speak				= race_table[race].speaks;
		mh->language		= race_table[race].understands;
		mh->alignment		= race_table[race].alignment;
		mh->ethos				= race_table[race].ethos;
		
		mh->max_hit		= dice(mh->level, race_type_table[race_table[race].type].hit_die);
		mh->max_hit		= UMAX(mh->level * race_type_table[race_table[race].type].hit_die / 2, mh->max_hit);
		mh->hit				= get_max_hit(mh);
		
		RESTRING(mh->name, format("summoned %s", race_table[race].race_name));
		RESTRING(mh->short_descr, format("a summoned %s", race_table[race].race_name));
		RESTRING(mh->long_descr, format("A summoned %s is here.", race_table[race].race_name));
		mh->npcdata->sac_timer = turn * level;
		
		if (!IS_EVIL(mh))
		{
			mh->npcdata->sac_string = STRALLOC("{178}$n shimmers and vanishes!");
			act( "{178}$n appears in a shimmer of light.", mh, NULL, NULL, TO_ALL);
		}
		else
		{
			mh->npcdata->sac_string = STRALLOC("{108}$n vanishes in a puff black smoke!");
			act( "{118}$n appears in a burst of hellish flames!", mh, NULL, NULL, TO_ALL);
		}
		char_reset(mh);
		
		if (!will_save(mh, ch, level, gsn_gate))
		{
			SET_BIT(mh->affected_by , AFF_DOMINATE);
// 			add_follower(mh, ch);
			if (who_fighting(ch))
				fight(mh, who_fighting(ch));
		}
		else
		{
			act("Unmoved by your pleas, $N returns through the gate.", ch, NULL, mh, TO_ALL);
			junk_mob(mh);
		}
		--count;
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_grasping_hand)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_grasping_hand(%p,%p,%p,%p)",sn,level,ch,vo);

	if (victim == ch)
	{
		send_to_char_color("You can't heavy hand yourself.\n\r", ch);
		pop_call();
		return FALSE;
	}
	
	if (victim->grappling || victim->grappled_by
	|| is_affected(victim, gsn_crushing_hand) || is_affected(victim, gsn_grasping_hand))
	{
		act("$N is already grappling.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (IS_AFFECTED(victim, AFF_FREEDOM))
	{
		act("You slip past a giant hand's grasp!", victim, NULL, NULL, TO_VICT);
		act("$n slips past a giant hand's grasp!", victim, NULL, NULL, TO_ROOM);
		pop_call();
		return TRUE;
	}

	if (combat_maneuver_check(ch, victim, sn) >= 0)
	{
		act("A giant hand grasps you firmly!", victim, NULL, NULL, TO_CHAR);
		act("A giant hand grasps $N firmly!", victim, NULL, NULL, TO_ROOM);
		if (sn == gsn_crushing_hand)
		{
			damage(ch, victim, dice(2,6)+12, sn, NULL);
		}
	}
	if (!valid_fight(ch, victim))
	{
		pop_call();
		return TRUE;
	}

	af.type      = sn;
	af.duration  = level;
	af.location  = 0;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_join( ch, victim, &af );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_harden)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_harden(%p,%p,%p,%p)",sn,level,ch,vo);

	if (target == TAR_CHAR_DEFENSIVE)
	{
		if (race_type(victim) != RTYPE_CONSTRUCT)
		{
			act("$N is not a construct.", ch, NULL, victim, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (is_affected(victim, gsn_harden))
		{
			act("$N is already hardened.", ch, NULL, victim, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
	
		act( "{178}Your defenses are further hardened.", victim, NULL, NULL, TO_CHAR);
		act( "{178}$n's defenses are further hardened.", victim, NULL, NULL, TO_ROOM);

		af.type      = sn;
		af.duration  = turn * level;
		af.location  = APPLY_DR_NONE;
		af.modifier  = level/2;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = AFF_NONE;
		af.level     = level;
		affect_to_char( ch, victim, &af );
	}
	else if (target == TAR_OBJ_INV)
	{
		if (is_obj_affected(obj, gsn_harden))
		{
			act( "$p is already hardened.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
	
		af.type      = sn;
		af.duration  = -1;
		af.location  = APPLY_NONE;
		af.modifier  = level/2;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = 0;
		af.level     = level;
		affect_to_obj( ch, obj, &af );

		act( "$p becomes more resilient.", ch, obj, NULL, TO_CHAR);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_harm)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_harm(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	dam = 10 * UMIN(level, 15);

	if (IS_UNDEAD(victim))
	{
		victim->hit = UMIN( victim->hit + dam, get_max_hit(victim));
		update_pos(victim,-1);

		act( "{138}A healing energy fills your body.", victim, NULL, NULL, TO_CHAR);
		act( "{138}A healing energy fills $N's body.",  victim, NULL, NULL, TO_ROOM);
	}
	else
	{
		spell_damage( ch, victim, dam, sn, level );
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_heal)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	DISEASE_DATA *dis, *dis_next;
	int heal;

	push_call("spell_heal(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	heal = 10 * UMIN(level, 15);
	
	if (is_affected(victim, gsn_festering_wounds))	
		heal /= 2;

	if (IS_UNDEAD(victim))
	{
		spell_damage( ch, victim, heal, sn, level );
	}
	else
	{
		act( "{138}A healing energy fills your body.", victim, NULL, NULL, TO_CHAR);
		act( "{138}A healing energy fills $n's body.", victim, NULL, NULL, TO_ROOM);
		for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;
			
			if (IS_SET(paf->bitvector, AFF_BLIND|AFF_DEAF|AFF_POISON))
			{
				affect_strip(victim,paf->type);
			}
			else if (IS_SET(paf->bitvector, AFF2_CONFUSION|AFF2_DAZED|AFF2_DAZZLED|AFF2_DROWNING|AFF2_NAUSEATED|AFF2_SICKENED|AFF2_STUNNED))
			{
				affect_strip(victim,paf->type);
			}

			switch (paf->location)
			{
				case APPLY_STR_DAMAGE:
				case APPLY_DEX_DAMAGE:
				case APPLY_CON_DAMAGE:
				case APPLY_INT_DAMAGE:
				case APPLY_WIS_DAMAGE:
				case APPLY_CHA_DAMAGE:
					affect_strip(victim,paf->type);
					break;
			}
		}
		affect_strip(victim,gsn_bleed_damage);
		affect_strip(victim,gsn_poison);
		affect_strip( victim, gsn_feeblemind );
		affect_strip( victim, gsn_festering_wounds );
		if (victim->poison != NULL)
		{
			POISON_DATA *npd, *pd;

			pd = victim->poison;
			victim->poison = NULL;

			while (pd != NULL)
			{
				npd = pd->next;
				FREEMEM( pd );
				pd = npd;
			}
		}
		for ( dis = victim->first_disease; dis != NULL; dis = dis_next )
		{
			dis_next = dis->next;
			disease_from_char(ch, dis);
		}
		if (domain_apotheosis(ch, DOMAIN_HEALING))
		{
			victim->hit = get_max_hit(victim);
			victim->nonlethal = 0;
		}
		else
		{
			victim->hit = UMIN( victim->hit + heal, get_max_hit(victim));
			victim->nonlethal = UMAX(0, victim->nonlethal - heal);
		}
		victim->move = get_max_move(victim);
		update_pos(victim,-1);
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_mass_heal)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	DISEASE_DATA *dis, *dis_next;
	CHAR_DATA *gch, *gch_next;
	int heal, cnt;

	push_call("spell_mass_heal(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	heal = 10 * UMIN(level, 25);

	for (cnt = 0, gch = victim->in_room->first_person ; gch ; gch = gch_next)
	{
		gch_next = gch->next_in_room;

		if (IS_UNDEAD(gch))
		{
			if (!can_mass_cast(ch, gch, sn))
			{
				continue;
			}
			if (cnt >= level)
			{
				break;
			}
			spell_damage( ch, gch, heal, sn, level );
			cnt++;
			continue;
		}

		if (!is_same_group(gch, victim))
		{
			continue;
		}
		if (cnt >= level)
		{
			break;
		}

		act( "{138}A healing energy fills your body.", gch, NULL, NULL, TO_CHAR);
		act( "{138}A healing energy fills $n's body.", gch, NULL, NULL, TO_ROOM);

		for ( paf = gch->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;
			
			if (IS_SET(paf->bitvector, AFF_BLIND|AFF_DEAF|AFF_POISON))
			{
				affect_strip(victim,paf->type);
			}
			else if (IS_SET(paf->bitvector, AFF2_CONFUSION|AFF2_DAZED|AFF2_DAZZLED|AFF2_NAUSEATED|AFF2_SICKENED|AFF2_STUNNED|AFF2_DROWNING))
			{
				affect_strip(victim,paf->type);
			}

			switch (paf->location)
			{
				case APPLY_STR_DAMAGE:
				case APPLY_DEX_DAMAGE:
				case APPLY_CON_DAMAGE:
				case APPLY_INT_DAMAGE:
				case APPLY_WIS_DAMAGE:
				case APPLY_CHA_DAMAGE:
					affect_strip(gch,paf->type);
					break;
			}
		}
		affect_strip(gch,gsn_bleed_damage);
		affect_strip(gch,gsn_poison);
		affect_strip( gch, gsn_feeblemind );
		affect_strip( gch, gsn_festering_wounds );
		if (gch->poison != NULL)
		{
			POISON_DATA *npd, *pd;

			pd = gch->poison;

			while (pd != NULL)
			{
				npd = pd->next;
				FREEMEM( pd );
				pd = npd;
			}
		}
		for ( dis = gch->first_disease; dis != NULL; dis = dis_next )
		{
			dis_next = dis->next;
			disease_from_char(gch, dis);
		}
		gch->hit = UMIN( gch->hit + heal, get_max_hit(gch));
		gch->nonlethal = UMAX(0, gch->nonlethal - heal);

		gch->move = get_max_move(gch);
		cnt++;
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_heal_mount)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	DISEASE_DATA *dis, *dis_next;
	int heal;

	push_call("spell_heal_mount(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!IS_ACT(victim, ACT_WARHORSE) || !is_master(victim, ch))
	{
		act("You can only use this spell on your divine mount.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	heal = 10 * UMIN(level, 15);

	act( "{138}You channel energy to heal your mount.", ch, NULL, NULL, TO_CHAR);
	act( "{138}$N channels energy to heal $s mount.", ch, NULL, NULL, TO_ROOM);
	
	for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;
		
			if (IS_SET(paf->bitvector, AFF_BLIND|AFF2_CONFUSION|AFF2_DAZED|AFF2_DAZZLED|AFF_DEAF|AFF_POISON))
			{
				affect_strip(victim,paf->type);
			}
			else if (IS_SET(paf->bitvector, AFF2_NAUSEATED|AFF2_SICKENED|AFF2_STUNNED))
			{
				affect_strip(victim,paf->type);
			}

		switch (paf->location)
		{
			case APPLY_STR_DAMAGE:
			case APPLY_DEX_DAMAGE:
			case APPLY_CON_DAMAGE:
			case APPLY_INT_DAMAGE:
			case APPLY_WIS_DAMAGE:
			case APPLY_CHA_DAMAGE:
				affect_strip(victim,paf->type);
				break;
		}
	}
	affect_strip(victim,gsn_bleed_damage);
	affect_strip(victim,gsn_poison);
	affect_strip(victim, gsn_feeblemind);
	affect_strip(victim, gsn_festering_wounds);
	if (victim->poison != NULL)
	{
		POISON_DATA *npd, *pd;

		pd = victim->poison;
		victim->poison = NULL;

		while (pd != NULL)
		{
			npd = pd->next;
			FREEMEM( pd );
			pd = npd;
		}
	}
	for ( dis = victim->first_disease; dis != NULL; dis = dis_next )
	{
		dis_next = dis->next;
		disease_from_char(victim, dis);
	}
	victim->hit = UMIN( victim->hit + heal, get_max_hit(victim));
	victim->move = UMIN( victim->move + heal, get_max_move(victim));
	victim->nonlethal = UMAX(0, victim->nonlethal - heal);
	update_pos(victim,-1);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_heat_metal)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	sh_int count;

	push_call("spell_heat_metal(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	af.type		= sn;
	af.duration	= 7;
	af.bittype	= AFFECT_TO_NONE;
	af.bitvector	= AFF_NONE;
	af.location	= APPLY_NONE;
	af.modifier	= 0;
	af.level     = level;
	
	if (wears_metal(victim, TRUE))
	{
		if (!save_resist(ch, victim, sn, level))
		{
			affect_join( ch, victim, &af);
			if (sn == gsn_heat_metal)
				act("{018}The metal you wear starts to get very warm!", victim, NULL, NULL, TO_CHAR);
			else
				act("{168}The metal you wear starts to get very chilly!", victim, NULL, NULL, TO_CHAR);
		}
	}
	
	if ((count = level/2) <= 1)
	{
		pop_call();
		return TRUE;
	}
	
	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (count <= 1)
			break;

		if (vch == victim)
			continue;

		if (!can_mass_cast(ch, vch, sn))
			continue;

		if (!wears_metal(victim, TRUE))
			continue;

		if (save_resist(ch, victim, sn, level))
			continue;

		affect_join(ch, vch, &af);

		if (sn == gsn_heat_metal)
			act("{018}The metal you wear starts to get very warm!", vch, NULL, NULL, TO_CHAR);
		else
			act("{168}The metal you wear starts to get very chilly!", vch, NULL, NULL, TO_CHAR);

		--count;
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_heroes_feast)
{
	CHAR_DATA *fch;
	AFFECT_DATA af;
	DISEASE_DATA *dis, *dis_next;
	bool cured = FALSE;
	int caster_check;

	push_call("spell_heroes_feast(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "$n creates a large selection of food and drinks.",     ch, NULL, NULL, TO_ROOM );
	act( "You create a large selection of food and drinks.",     ch, NULL, NULL, TO_CHAR );
	act( "Everyone joins you as you eat your fill.", ch, NULL, NULL, TO_CHAR );

	caster_check = spell_dice(ch, sn, 1, 20) + level;

	for (fch = ch->in_room->first_person ; fch ; fch = fch->next_in_room)
	{
		if (must_eat(fch) && fch->position >= POS_RESTING)
		{
			gain_condition( fch, COND_FULL, 50 );
			gain_condition( fch, COND_THIRST, 50 );
			send_to_char( "You join in the feast and eat your fill.\n\r", fch );

			if (IS_AFFECTED(fch, AFF2_SICKENED))
			{
				AFFECT_STRIP(fch, AFF2_SICKENED);
				cured = TRUE;
			}
			if (IS_AFFECTED(fch, AFF2_NAUSEATED))
			{
				AFFECT_STRIP(fch, AFF2_NAUSEATED);
				cured = TRUE;
			}
			
			if (is_affected( fch, gsn_poison))
			{
				if (caster_check >= 10 + get_affect_level(fch, gsn_poison))
				{
					affect_strip( fch, gsn_poison );
					cured = TRUE;
				}
			}
		
			if (fch->poison != NULL)
			{
				POISON_DATA *npd, *pd;
		
				pd = fch->poison;
				
				if (caster_check >= (pd->dc ? pd->dc : poison_table[pd->type].dc))
				{	
					while (pd != NULL)
					{
						npd = pd->next;
						FREEMEM( pd );
						pd = npd;
					}
					cured = TRUE;
				}
				else
				{
					cured = FALSE;
				}
			}	
			
			if (fch->first_disease != NULL)
			{
				for ( dis = fch->first_disease; dis != NULL; dis = dis_next )
				{
					dis_next = dis->next;
					
					if (dis->dc && caster_check < dis->dc)
						continue;
					if (!dis->dc && caster_check < disease_table[dis->type].dc)
						continue;
	
					disease_from_char(fch, dis);
				}
				if (!fch->first_disease)
					cured = TRUE;
			}

			if ( cured )
			{
				act( "A warm feeling runs through your body.", fch, NULL, NULL, TO_CHAR );
			}
	
			if (is_affected( fch, sn ))
			{
				continue;
			}
							
			af.type      = sn;
			af.duration  = 12 * hr;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = 0;
			af.level     = level;

			af.location  = APPLY_HIT;
			af.modifier  = spell_dice(ch, sn, 1, 8) + UMIN(level/2, 10);
			affect_to_char( ch, fch, &af );

			af.location  = APPLY_MOR_TOHIT;
			af.modifier  = 1;
			affect_to_char( ch, fch, &af );

			af.location  = APPLY_MOR_WILL;
			af.modifier  = 1;
			affect_to_char( ch, fch, &af );

			af.location  = APPLY_SAVE_POISON;
			af.modifier  = 4;
			affect_to_char( ch, fch, &af );
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_hold)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;

	push_call("spell_hold(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;
		
		if (is_same_group(ch, vch))
			continue;
			
		if ((sn == gsn_hold_animal || sn == gsn_hold_person
		|| sn == gsn_hold_monster) && vch != victim)
			continue;

		if (!can_mass_cast(ch, vch, sn))
			continue;

		if (((sn == gsn_hold_plant || sn == gsn_mass_hold_plant) && race_type(vch) != RTYPE_PLANT)
		|| ((sn == gsn_hold_animal || sn == gsn_mass_hold_animal) && !IS_ANIMAL(vch))
		|| ((sn == gsn_hold_person || sn == gsn_mass_hold_person) && !IS_HUMANOID(vch)))
		{
			act( "$N seems to be unaffected by your magic.", ch, NULL, vch, TO_CHAR );
			continue;
		}
	
		if (save_resist(ch, vch, sn, level))
			continue;
		
		af.type      = sn;
		af.duration  = level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_PARALYSIS;
		af.level     = level;
		affect_to_char( ch, vch, &af );
	
		act( "You freeze in your tracks!", vch, NULL, NULL, TO_CHAR );
		act( "$n freezes in $s tracks!",  vch, NULL, NULL, TO_ROOM );
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_horrid_wilting)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	int dam, sizedie;
	int save;

	push_call("spell_horrid_wilting(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act("{108}Moisture is sucked rapidly out of the area!", victim, NULL, NULL, TO_ALL);

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn))
			continue;
			
		if (IS_INCORPOREAL(vch))
			continue;

		switch (race_type(vch))
		{
			case RTYPE_UNDEAD:
			case RTYPE_CONSTRUCT:
				continue;
			case RTYPE_PLANT:
			case RTYPE_OOZE:
				sizedie = 8;
				break;
			default:
				sizedie = 6;
				break;
		}
			
		if (rspec_req(vch, RSPEC_WATER))
			sizedie = 8;

		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
			continue;

		dam = spell_dice(ch, sn, UMIN(level,20), sizedie);

		if (save == PARTIAL)
			dam /= 2;

		damage( ch, vch, dam, sn, NULL );
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_ice_storm)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	ROOM_TIMER_DATA rtd;
	int diceroll;

	push_call("spell_ice_storm(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	diceroll = spell_dice(ch, sn, 5, 6);

	act( "$n {178}calls down a storm of ice!", ch, NULL, NULL, TO_ROOM);
	act( "{178}You call down a storm of ice!", ch, NULL, NULL, TO_CHAR);

	spell_damage(ch, victim, diceroll, sn, level);

	if (IS_SET(ch->in_room->room_flags, ROOM_ICE))
	{
		pop_call();
		return TRUE;
	}

	switch (ch->in_room->sector_type)
	{
		case SECT_ETHEREAL:
		case SECT_ASTRAL:
		case SECT_UNDER_WATER:
			pop_call();
			return TRUE;
	}

	rtd.vnum			= ch->in_room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= level;
	rtd.bitvector	= ROOM_ICE;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_implosion)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	int cnt, save;

	push_call("spell_implosion(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	for (cnt = 0, vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn) || !is_same_group(victim, vch))
		{
			continue;
		}

		if (!IS_LIVING(vch))
			continue;

		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
			continue;
			
		cnt++;
		
		if (cnt >= 4)
			break;

		if (save == PARTIAL)
		{
			damage(ch, vch, spell_dice(ch, sn, 3, 6) + level, sn, NULL);
		}
		else
		{
			damage(ch, vch, level * 10, sn, NULL);
		}
	}
	
	if (!cnt)
	{
		act("Your spell had no effect on anyone.", ch, NULL, NULL, TO_CHAR);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_insect_plague)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int save, count;

	push_call("spell_insect_plague(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{038}A cloud of flying insects swarms in!", ch, NULL, NULL, TO_ALL );

	for (count = UMIN(level/3, 6), vch = victim->in_room->first_person ; vch ; vch = vch_next)
	{
		vch_next = vch->next_in_room;

		if (count <= 0)
			break;

		if (!can_mass_cast(ch, vch, sn))
			continue;
		if (is_affected(vch, sn))
			continue;
		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
			continue;
				
		af.type      = sn;
		af.duration  = turn*level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.level     = level;

		if (save == PARTIAL)
		{
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
		}
		else
		{
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_SICKENED;
		}

		affect_to_char( ch, vch, &af );
		
		act( "{038}The insects swarm around you!", vch, NULL, NULL, TO_CHAR );
		act( "{038}The insects swarm around $n!", vch, NULL, NULL, TO_ROOM );
		--count;
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_invis)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_invis(%p,%p,%p,%p)",sn,level,ch,vo);

	if (target == TAR_CHAR_DEFENSIVE)
	{
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
		act( "{108}You fade out of view.", victim, NULL, NULL, TO_CHAR);
		act( "{108}$n fades out of view.", victim, NULL, NULL, TO_ROOM);

		af.type      = sn;
		af.duration  = sn == gsn_improved_invis ? level : turn * level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF_INVISIBLE;
		af.level     = level;
		affect_join( ch, victim, &af );
		
		if (sn == gsn_improved_invis)
		{
			af.location = APPLY_CONCEALMENT;
			af.modifier = 50;
			affect_join( ch, victim, &af );
		}
	}
	else if (target == TAR_OBJ_INV)
	{
		if (IS_SET(obj->extra_flags, ITEM_INVIS))
		{
			act( "$p is already invisible.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}

		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
	
		af.type      = sn;
		af.duration  = turn * level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_OBJ;
		af.bitvector = ITEM_INVIS;
		af.level     = level;
		affect_to_obj( ch,obj, &af );

		act( "$p fades out of view.", ch, obj, NULL, TO_CHAR);
	}
	pop_call();
	return TRUE;
}


/* 
 * Knock spell, d20 style
 * homebrew function is a hack of do_pick 
 * Variant rule pits caster level check + 10 vs. lock DC - Kregor
 */
DO_SPELL(spell_knock)
{
	char arg[MAX_INPUT_LENGTH];
	OBJ_DATA *obj;
	int door, diceroll, pick_lvl;
	
	target_name = one_argument(target_name, arg);

	if ((door = find_door(ch, arg)) >= 0)
	{
		ROOM_INDEX_DATA *to_room;
		EXIT_DATA *pExit, *pExit_rev;

		pExit = ch->in_room->exit[door];

		if (!IS_SET(pExit->exit_info, EX_CLOSED))
		{
			send_to_char("It's not even closed. That was a waste.\n\r", ch);
			pop_call();
			return FALSE;
		}
	
		if (!IS_SET(pExit->exit_info, EX_LOCKED)
		|| IS_SET(pExit->exit_info, EX_KNOCKED))
		{
			send_to_char("It's already unlocked.\n\r", ch);
			pop_call();
			return FALSE;
		}
	
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
	
		if (IS_SET(pExit->exit_info, EX_PICKPROOF))
		{
			pop_call();
			return TRUE;
		}
	
		if (IS_SET(pExit->exit_info, EX_AMAZING_PICK))
			pick_lvl = 40;
		else if (IS_SET(pExit->exit_info, EX_HARD_PICK))
			pick_lvl = 30;
		else if (IS_SET(pExit->exit_info, EX_EASY_PICK))
			pick_lvl = 20;
		else
			pick_lvl = 25;
	
		if (IS_SET(pExit->exit_info, EX_MAGICAL_LOCK))
			pick_lvl += 10;
	
		diceroll = spell_dice(ch, sn, 1, 20) + 10 + level;

		if (diceroll < pick_lvl)
		{
			pop_call();
			return TRUE;
		}
	
		act( "{138}The lock on the $d glows momentarily.", ch, pExit->keyword, NULL, TO_ROOM);
		act( "{138}The lock on the $d glows momentarily.", ch, pExit->keyword, NULL, TO_CHAR);
	
		SET_BIT(pExit->exit_info, EX_KNOCKED);
		/*
		 * knock the other side
		 */
		if ((to_room = room_index[pExit->to_room]) != NULL
		&&  (pExit_rev = to_room->exit[rev_dir[door]]) != NULL
		&&   room_index[pExit_rev->to_room] == ch->in_room)
		{
			SET_BIT( pExit_rev->exit_info, EX_KNOCKED );
		}
	}
	else if ((obj = get_obj_here(ch, arg)) != NULL)
	{
		if (obj->item_type != ITEM_CONTAINER)
		{
			act( "$p is not a container.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}

		if (!IS_SET(obj->value[1], CONT_CLOSED))
		{
			act( "$p is not closed.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}

		if (!IS_SET(obj->value[1], CONT_LOCKED) || IS_SET(obj->value[1], CONT_KNOCKED))
		{
			act( "$p is already unlocked.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}

		if (IS_SET(obj->value[1], CONT_AMAZING_PICK))
			pick_lvl = 40;
		else if (IS_SET(obj->value[1], CONT_HARD_PICK))
			pick_lvl = 30;
		else if (IS_SET(obj->value[1], CONT_EASY_PICK))
			pick_lvl = 20;
		else
			pick_lvl = 25;
		if (IS_SET(obj->value[1], CONT_MAGICAL_LOCK))
			pick_lvl += 10;

		diceroll = spell_dice(ch, sn, 1, 20) + 3 + level;

		if (diceroll < pick_lvl)
		{
			pop_call();
			return TRUE;
		}	
		SET_BIT(obj->value[1], CONT_KNOCKED);
		REMOVE_BIT(obj->value[1], CONT_CLOSED);

		act( "{138}$p pops open.", ch, obj, NULL, TO_ROOM);
		act( "{138}$p pops open.", ch, obj, NULL, TO_CHAR);
	}
	else
	{
		act( "You see no $T here.", ch, NULL, arg, TO_CHAR );
		pop_call();
		return FALSE;
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_light)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_light(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_OBJ_STAT(obj, ITEM_GLOW))
	{
		send_to_char( "That object already glows.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	af.type				= sn;
	af.duration		= sn == gsn_cont_light ? -1 : (turn * level);
	af.bittype		= AFFECT_TO_OBJ;
	af.bitvector	= ITEM_GLOW;
	af.location		= APPLY_ROOM_LIGHT;
	af.modifier		= 1;
	af.level    	= level;
	affect_to_obj( ch,obj, &af);

	act( "$p {138}begins to glow with a soft light.", ch, obj, NULL, TO_CHAR);
	act( "$p {138}begins to glow with a soft light.", ch, obj, NULL, TO_ROOM);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_lightning_bolt)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_lightning_bolt(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	dam = spell_dice(ch, sn, UMIN(level, 10), 6);

	spell_damage(ch, victim, dam, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_locate_creature)
{
	CHAR_DATA *victim;
	int count, race, casterlvl;
	char txt[MAX_STRING_LENGTH];

	push_call("spell_locate_creature(%p,%p,%p,%p)",sn,level,ch,vo);

	if (*target_name == '\0')
	{
		send_to_char( "Who are you trying to locate?\n\r", ch );
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	casterlvl = dice(1,20) + level;
	*txt = '\0';

	for (count = 0, victim = mud->f_char ; victim ; victim = victim->next)
	{
		if (!can_see_world(ch, victim))
		{
			continue;
		}
		
		if (!is_multi_name_list_short(target_name, PERS(victim, ch)))
		{
			if (!is_multi_name_list_short(target_name, adjective(victim)))
			{
				if ((race = lookup_race(target_name)) == -1 || get_race(victim) != race)
				{
					continue;
				}
			}
		}
		
		if (!victim->in_room)
		{
			continue;
		}
		
		//flowing water blocks spell
		if (victim->in_room->sector_type == SECT_LAKE
		|| victim->in_room->sector_type == SECT_RIVER
		|| victim->in_room->sector_type == SECT_OCEAN
		|| victim->in_room->sector_type == SECT_UNDER_WATER)
		{
			continue;
		}

		if (IS_SET(victim->in_room->room_flags, ROOM_NOSCRY))
		{
			continue;
		}
		
		if (CAN_ETHEREAL_WALK(victim) || CAN_ASTRAL_WALK(victim))
		{
			continue;
		}
		
		if (check_nondetection(ch, victim, gsn_locate_creature))
		{
			continue;
		}
		
		if (is_affected(victim, gsn_mislead))
		{
			continue;
		}
		
		cat_sprintf(txt, "%s is at %s in %s.\n\r", PERS(victim, ch), victim->in_room->name, victim->in_room->area->name);
		
		if (++count >= UMIN(level/2, 10))
		{
			break;
		}
	}

	if (count == 0)
	{
		send_to_char( "You find no one like that in hell, earth, or heaven.\n\r", ch );
	}
	else
	{
		send_to_char( txt, ch );
	}

	pop_call();
	return TRUE;
}


DO_SPELL(spell_locate_object)
{
	OBJ_DATA *obj;
	OBJ_DATA *in_obj;
	char obj_name[MAX_INPUT_LENGTH];
	char txt[MAX_STRING_LENGTH];
	int count;

	push_call("spell_locate_object(%p,%p,%p,%p)",sn,level,ch,vo);

	if (*target_name == '\0')
	{
		send_to_char( "What are you trying to locate?\n\r", ch );
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	*txt = '\0';

	for (count = 0, obj = mud->f_obj ; obj ; obj = obj->next)
	{
		if (!is_multi_name_list_short(target_name, obj->short_descr) || !can_see_obj(ch, obj) )
		{
			continue;
		}
		
		if (IS_SET(obj->extra_flags, ITEM_NOSCRY))
		{
			continue;
		}

		if (obj->in_room && IS_SET(obj->in_room->room_flags, ROOM_NOSCRY))
		{
			continue;
		}

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

		if (in_obj->carried_by && !can_see_world(ch, in_obj->carried_by))
		{
			continue;
		}

		if (in_obj->carried_by)
		{
			if (check_nondetection(ch, in_obj->carried_by, gsn_locate_object))
			{
				continue;
			}
		}
		else if (!obj->in_room)
		{
			continue;
		}

		if (in_obj->carried_by->in_room && IS_SET(in_obj->carried_by->in_room->room_flags, ROOM_NOSCRY))
		{
			continue;
		}

		if (in_obj->material == MATERIAL_LEAD)
		{
			continue;
		}
		
		strcpy(obj_name, capitalize(obj->short_descr));

		if (in_obj->carried_by != NULL)
		{
			cat_sprintf(txt, "%s {300}is carried by %s.\n\r", obj_name, PERS(in_obj->carried_by, ch));
		}
		else
		{
			cat_sprintf(txt, "%s {300}is at %s.\n\r", obj_name, in_obj->in_room->name);
		}
		if (++count >= UMIN(level/2, 10))
		{
			break;
		}
	}

	if (count == 0)
	{
		send_to_char( "You find nothing like that in hell, earth, or heaven.\n\r", ch );
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_make_whole)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	int mend;

	push_call("spell_make_whole(%p,%p,%p,%p)",sn,level,ch,vo);

	if (target == TAR_CHAR_DEFENSIVE)
	{
		if (race_type(victim) != RTYPE_CONSTRUCT)
		{
			act("$N is not a construct!", ch, NULL, victim, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
		mend = spell_dice(ch, sn, UMIN(level, 5), 6);
		victim->hit = UMIN( victim->hit + mend, get_max_hit(victim));
		update_pos(victim,-1);
		
		act("$n's scars and breaks mend themselves.", victim, NULL, NULL, TO_ROOM);
		act("Your scars and breaks mend themselves.", victim, NULL, NULL, TO_CHAR);
	}
	else if (target == TAR_OBJ_INV)
	{
		if (obj->weight > 100 * level)
		{
			act( "$p is too large to be mended by this spell.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
		mend = spell_dice(ch, sn, 1, 6) + level;
		
		if (obj->hit_points + mend >= get_obj_max_hit(obj))
		{
			act( "$p {138}mends as good as new!", ch, obj, NULL, TO_CHAR);
			act( "$p {138}mends as good as new!", ch, obj, NULL, TO_ROOM);
		}
		else
		{
			act( "$p {138}is mended.", ch, obj, NULL, TO_CHAR);
			act( "$p {138}is mended.", ch, obj, NULL, TO_ROOM);
		}
		
		obj->hit_points = UMIN(obj->hit_points + mend, get_obj_max_hit(obj));
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_magic_missile)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int count;

	push_call("spell_magic_missile(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	count = (level - 1) / 2 + 1;
	
	if (count == 1)
	{
		act( "{158}A bolt of force shoots from $n's finger...", ch, NULL, victim, TO_ROOM );
		act( "{158}A bolt of force shoots from your finger...", ch, NULL, victim, TO_CHAR );
	}
	else
	{
		act( "{158}Bolts of force shoot from $n's fingers...", ch, NULL, victim, TO_ROOM );
		act( "{158}Bolts of force shoot forth from your fingers...", ch, NULL, victim, TO_CHAR );
	}

	while (count)
	{
		if (!save_resist(ch, victim, sn, level))
		{
			damage( ch, victim, spell_dice(ch, sn, 1,4)+1, sn, NULL );
		}
		--count;
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_magic_vestment)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	OBJ_DATA *armor;
	AFFECT_DATA af;

	push_call("spell_magic_vestment(%p,%p,%p,%p)",sn,level,ch,vo);

	if (target == TAR_CHAR_DEFENSIVE)
	{
		// get_eq_char will not work here, since the armor could be layered - Kregor
		for (armor = victim->first_carrying ; armor ; armor = armor->next_content)
		{
			if (IS_WORN(armor) && IS_OBJ_TYPE(armor, ITEM_ARMOR) && !IS_OBJ_STAT(armor, ITEM_MAGIC) && (WEAR_LOC(armor, WEAR_SHIELD) || WEAR_LOC(armor, WEAR_BODY)))
				break;
		}
		if (!armor)
		{
			act( "$N is not wearing a suitable vestment.", ch, NULL, victim, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
		af.type		= sn;
		af.duration	= turn * level;
		af.bittype	= AFFECT_TO_OBJ;
		af.level     = level;
		af.bitvector	= ITEM_MAGIC;	
		af.location	= APPLY_ENHANCE_AC;
		af.modifier 	= URANGE(1, level/4, 5);
		affect_to_obj(ch, armor, &af);
	
		act( "$p {138}glows {078}momentarily.", ch, obj, NULL, TO_CHAR);
		act( "$p {138}glows {078}momentarily.", ch, obj, NULL, TO_ROOM);
	}
	else if (target == TAR_OBJ_INV)
	{
		if (obj->item_type != ITEM_ARMOR || IS_OBJ_STAT(obj, ITEM_MAGIC))
		{
			act( "$p cannot be enchanted.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
		af.type		= sn;
		af.duration	= turn * level;
		af.bittype	= AFFECT_TO_OBJ;
		af.level     = level;
		af.bitvector	= ITEM_MAGIC;	
		af.location	= APPLY_ENHANCE_AC;
		af.modifier 	= URANGE(1, level/4, 5);
		affect_to_obj(ch, obj, &af);
	
		act( "$p {138}glows {078}momentarily.", ch, obj, NULL, TO_CHAR);
		act( "$p {138}glows {078}momentarily.", ch, obj, NULL, TO_ROOM);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_magic_weapon)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_magic_weapon(%p,%p,%p,%p)",sn,level,ch,vo);

	if (target == TAR_CHAR_DEFENSIVE)
	{
		if ((obj = get_wield(victim, FALSE)) == NULL)
		{
			if (!learned(victim, gsn_martial_arts))
			{
				act( "$N's hands are not that deadly.", ch, NULL, victim, TO_CHAR);
				pop_call();
				return FALSE;
			}
			if (!ForReal)
			{
				pop_call();
				return TRUE;
			}
			af.type		= sn;
			af.duration	= turn * level;
			af.bittype	= AFFECT_TO_NONE;
			af.level     = level;
			af.bitvector	= AFF_NONE;
		
			af.location	= APPLY_HITROLL;
			af.modifier 	= URANGE(1, level/4, 5);
			affect_join( ch, victim, &af);
		
			af.location	= APPLY_DAMROLL;
			af.modifier 	= URANGE(1, level/4, 5);
			affect_join( ch, victim, &af);
		
			act( "$n's hands {138}glow {078}momentarily.", ch, NULL, NULL, TO_CHAR);
			act( "$n's hands {138}glow {078}momentarily.", ch, NULL, NULL, TO_ROOM);
		}
		else
		{
			if (!IS_OBJ_TYPE(obj, ITEM_WEAPON))
			{
				act( "$p is not a weapon.", ch, obj, NULL, TO_CHAR);
				pop_call();
				return FALSE;
			}
			if (IS_OBJ_STAT(obj, ITEM_MAGIC))
			{
				act( "$p is already enchanted.", ch, obj, NULL, TO_CHAR);
				pop_call();
				return FALSE;
			}
			if (!ForReal)
			{
				pop_call();
				return TRUE;
			}
			af.type		= sn;
			af.duration	= turn * level;
			af.bittype	= AFFECT_TO_OBJ;
			af.level     = level;
			af.bitvector	= ITEM_MAGIC;
		
			af.location	= APPLY_HITROLL;
			af.modifier 	= URANGE(1, level/4, 5);
			affect_to_obj( ch,obj, &af);
		
			af.location	= APPLY_DAMROLL;
			af.modifier 	= URANGE(1, level/4, 5);
			affect_to_obj( ch,obj, &af);
		
			act( "$p {138}glows {078}momentarily in your hand.", victim, obj, NULL, TO_CHAR);
			act( "$p {138}glows {078}momentarily in $n's hand.", victim, obj, NULL, TO_ROOM);
		}
	}
	else if (target == TAR_OBJ_INV)
	{
		if (!IS_OBJ_TYPE(obj, ITEM_WEAPON))
		{
			act( "$p is not a weapon.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (IS_OBJ_STAT(obj, ITEM_MAGIC))
		{
			act( "$p is already enchanted.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (!ForReal)
		{
			pop_call();
			return TRUE;
		}
		af.type		= sn;
		af.duration	= turn * level;
		af.bittype	= AFFECT_TO_OBJ;
		af.level     = level;
		af.bitvector	= ITEM_MAGIC;
	
		af.location	= APPLY_HITROLL;
		af.modifier 	= URANGE(1, level/4, 5);
		affect_to_obj( ch,obj, &af);
	
		af.location	= APPLY_DAMROLL;
		af.modifier 	= URANGE(1, level/4, 5);
		affect_to_obj( ch,obj, &af);
	
		act( "$p {138}glows {078}momentarily in your hand.", ch, obj, NULL, TO_CHAR);
		act( "$p {138}glows {078}momentarily in $n's hand.", ch, obj, NULL, TO_ROOM);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_major_image)
{
	CHAR_DATA *mh, *victim;
	char illusionname[MAX_INPUT_LENGTH];
	int i;

	push_call("spell_major_image(%p,%p,%p,%p)",sn,level,ch,vo);

	for (i = 0 ; target_name[i] != '\0' ; i++)
	{
		if (ispunct(target_name[i]) || isdigit(target_name[i]))
		{
			send_to_char("You cannot create such an abstract illusion.\n\r", ch);
			pop_call();
			return FALSE;
		}
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	mh	= create_mobile( get_mob_index( MOB_VNUM_ILLUSION ) );
	char_to_room( mh, ch->in_room->vnum, TRUE );

	mh->level				= 1;
	mh->npcdata->sac_timer = level * turn;
	mh->npcdata->sac_string = STRALLOC("$n shimmers then winks out of existence.");
	mh->max_hit = mh->hit = 1;

	if (target_name[0] == '\0' || !strcasecmp(target_name, "self"))
	{
		sprintf(illusionname, "%s^", ch->name);
		RESTRING(mh->name, illusionname);

		if (IS_NPC(ch))
		{
			RESTRING(mh->short_descr, ch->short_descr);
		}
		else
		{
			RESTRING(mh->short_descr, ch->name);
		}
		RESTRING(mh->long_descr, ch->long_descr);
		RESTRING(mh->description, ch->description);
		mh->race = ch->race;
		mh->sex = ch->sex;
	}
	else if ((victim = get_char_room(ch, target_name)) != NULL && victim != mh)
	{
		for (illusionname[0] = '\0', i = 0 ; victim->name[i] != '\0' ; i++)
		{
			if (victim->name[i] == ' ')
			{
				strcat(illusionname, "^ ");
			}
			else
			{
				cat_sprintf(illusionname, "%c", victim->name[i]);
			}
		}
		strcat(illusionname, "^");

		RESTRING(mh->name, illusionname);
		if (IS_NPC(victim))
		{
			RESTRING(mh->short_descr, victim->short_descr);
		}
		else
		{
			RESTRING(mh->short_descr, victim->name);
		}
		if (IS_NPC(victim))
		{
			RESTRING(mh->long_descr, victim->long_descr);
		}
		else
		{
			RESTRING(mh->long_descr, victim->long_descr);
		}
		RESTRING(mh->description, victim->description);
		mh->race			= victim->race;
		mh->sex			= victim->sex;
	}
	else
	{
		for (illusionname[0] = '\0', i = 0 ; target_name[i] != '\0' ; i++)
		{
			if (target_name[i] == ' ')
			{
				strcat(illusionname, "^ ");
			}
			else
			{
				cat_sprintf(illusionname, "%c", target_name[i]);
			}
		}
		strcat(illusionname, "^");

		RESTRING(mh->name, illusionname);
		RESTRING(mh->short_descr, target_name);
		RESTRING(mh->long_descr, format("%s stands here.", target_name));
		RESTRING(mh->description, "");

		mh->race			= ch->race;
		mh->sex			= ch->sex;
	}

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

DO_SPELL(spell_mass_invis)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	CHAR_DATA *gch;
	int count;

	push_call("spell_mass_invis(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (count = 0, gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (count >= level)
			break;

		if (!is_same_group(gch, victim))
		{
			continue;
		}
		if (IS_AFFECTED(gch, AFF_INVISIBLE))
		{
			continue;
		}

		act( "$n slowly fades out of existence.", gch, NULL, NULL, TO_ROOM);
		act( "You slowly fade out of existence.", gch, NULL, NULL, TO_CHAR);
		af.type      = sn;
		af.duration  = turn * level;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF_INVISIBLE;
		af.level     = level;
		affect_to_char( ch, gch, &af );
		
		count++;
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_mass_refresh)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch;
	int move, cnt;

	push_call("spell_mass_refresh(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	move = spell_dice(ch, sn, 2, 8) + UMIN(level, 30);
	
	for (cnt = 0, gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (!is_same_group(gch, victim))
		{
			continue;
		}
		if (cnt >= level)
		{
			continue;
		}
		gch->move = UMIN(gch->move + move, get_max_move(gch));

		act( "{138}$n looks a bit more refreshed.", gch, NULL, NULL, TO_ROOM );
		act( "{138}You feel a bit more refreshed.", gch, NULL, NULL, TO_CHAR );
		cnt++;
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_mending)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	int mend;

	push_call("spell_mending(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (obj->weight > 10 * level)
	{
		act( "$p is too large to be mended by this cantrip.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (IS_OBJ_STAT(obj, ITEM_MAGIC))
	{
		act( "Magical items cannot be mended with this cantrip.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (IS_OBJ_STAT(obj, ITEM_BROKEN))
	{
		act( "$p is too badly damaged to mend.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (obj->hit_points == 0)
	{
		act( "$p is too badly damaged to mend.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	mend = dice(1,4);
	
	if (obj->hit_points + mend >= get_obj_max_hit(obj))
	{
		act( "$p {138}mends as good as new!", ch, obj, NULL, TO_CHAR);
		act( "$p {138}mends as good as new!", ch, obj, NULL, TO_ROOM);
	}
	else
	{
		act( "$p {138}mends slightly.", ch, obj, NULL, TO_CHAR);
		act( "$p {138}mends slightly.", ch, obj, NULL, TO_ROOM);
	}
	
	obj->hit_points = UMIN(obj->hit_points + mend, get_obj_max_hit(obj));

	pop_call();
	return TRUE;
}


/*
 * new approach to one-spell, multi-missile spells - Kregor
 */
DO_SPELL(spell_meteor_swarm)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	ROOM_INDEX_DATA *tar_room; //might need in case victim dies before spell runs out
	int diceroll, count, cnt;

	push_call("spell_meteor_swarm(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if ((tar_room = victim->in_room) == NULL)
	{
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act("{118}A hail of {138}fireballs {118}showers forth!", ch, NULL, NULL, TO_ALL);

	//Send out 1 ball for every 5 caster levels against the victim and allies...
	for (count = cnt = level/5 ; cnt ; cnt--)
	{
		if (count <= 0) //balls all gone
			break;

		for (vch = tar_room->last_person ; vch ; vch = vch_next)
		{
			vch_next = vch->prev_in_room;
			
			if (vch == victim || is_same_group(vch, victim))
			{
				//first check_hit for blunt trauma on target
				if(check_hit(ch, vch, dice(1,20), 0, sn, NULL, TRUE, TRUE))
				{
					act("{118}A fireball slams into you!", vch, NULL, NULL, TO_CHAR);
					act("{118}A fireball slams into $n!", vch, NULL, NULL, TO_ROOM);
					damage( ch, vch, spell_dice(ch, sn, 2, 6), gsn_bash_hit, NULL );
				}
				else
				{
					damage( ch, vch, 0, gsn_bash_hit, NULL );
				}
				//then make the ball of fire explode!
				diceroll = spell_dice(ch, sn, 6, 6);
				spell_damage(ch, vch, diceroll, sn, level);
				--count;

				if (count <= 0)
					break;
			}
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_mislead)
{
	CHAR_DATA *mh;
	char illusionname[MAX_INPUT_LENGTH];
	AFFECT_DATA af;

	push_call("spell_mislead(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	//create illusory double...
	mh	= create_mobile( get_mob_index( MOB_VNUM_ILLUSION ) );
	char_to_room( mh, ch->in_room->vnum, TRUE );

	mh->level									= 1;
	mh->npcdata->sac_timer		= level;
	mh->npcdata->sac_string		= STRALLOC("$n shimmers then winks out of existence.");
	mh->max_hit								= 1;
	mh->hit										= get_max_hit(mh);
	mh->race 									= ch->race;
	mh->sex 									= ch->sex;

	sprintf(illusionname, "%s^", ch->name);
	RESTRING(mh->name, illusionname);

	if (IS_NPC(ch))
	{
		RESTRING(mh->short_descr, ch->short_descr);
	}
	else
	{
		RESTRING(mh->short_descr, ch->name);
	}
	RESTRING(mh->long_descr, ch->long_descr);
	RESTRING(mh->description, ch->description);
	
	//then improved invis the caster...
	af.type      = sn;
	af.duration  = level;
	af.location  = APPLY_CONCEALMENT;
	af.modifier  = 50;
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF_INVISIBLE;
	af.level     = level;
	affect_join( ch, ch, &af );

	//make the illusion wander
	REMOVE_BIT(mh->act, ACT_SENTINEL|ACT_NOWANDER);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_neutralize_poison)
{
	CHAR_DATA	*victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	bool cured = FALSE;
	bool failed = FALSE;
	int caster_check;
	AFFECT_DATA af;

	push_call("spell_neutralize_poison(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	caster_check = spell_dice(ch, sn, 1, 20) + level;
	
	if (target == TAR_CHAR_DEFENSIVE)
	{
		if ( is_affected( victim, gsn_poison ) )
		{
			if (caster_check >= 10 + get_affect_level(victim, gsn_poison))
			{
				affect_strip( victim, gsn_poison );
				cured = TRUE;
			}
			else
			{
				act("A necromantic toxin resisted your spell.", ch, NULL, victim, TO_CHAR);
				failed = TRUE;
			}
		}
	
		if (victim->poison != NULL)
		{
			POISON_DATA *npd, *pd;
	
			pd = victim->poison;
			
			if (caster_check >= (pd->dc ? pd->dc : poison_table[pd->type].dc))
			{	
				while (pd != NULL)
				{
					npd = pd->next;
					FREEMEM( pd );
					pd = npd;
				}
				cured = TRUE;
			}
			else
			{
				act("The toxins have resisted your spell.", ch, NULL, victim, TO_CHAR);
				failed = TRUE;
			}
		}
		
		if (!IS_NPC(victim) && drunk_level(victim))
		{
			victim->pcdata->condition[COND_DRUNK] = 0;
			act( "You are feeling sober now.", victim, NULL, NULL, TO_CHAR );
			act( "$n looks less groggy.", victim, NULL, NULL, TO_ROOM );
		}
			
		if ( cured )
		{
			if (ch != victim)
			{
				act( "You have purged toxins from $N's body.", ch, NULL, victim, TO_CHAR );
			}
			act( "A warm feeling runs through your body.", victim, NULL, NULL, TO_CHAR );
		}
		else if (!failed)
		{	
			af.type      = sn;
			af.duration  = 5 * turn * level;
			af.location  = APPLY_IMM_POISON;
			af.modifier  = 1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			af.level     = level;
			affect_join( ch, victim, &af );
		
			act( "{128}You feel a ward against poison fill your body.", victim, NULL, NULL, TO_CHAR );
			if ( ch != victim )
				act( "{128}$N is filled with a ward against poisons.", ch, NULL, victim, TO_CHAR );
		}
	}
	else if (target == TAR_OBJ_INV)
	{
		if (obj->poison != NULL)
		{
			POISON_DATA *npd, *pd;
	
			pd = obj->poison;
	
			if (caster_check >= (pd->dc ? pd->dc : poison_table[pd->type].dc))
			{	
				while (pd != NULL)
				{
					npd = pd->next;
					FREEMEM( pd );
					pd = npd;
				}
				cured = TRUE;
			}
		}
	
		if ( cured )
		{
			act( "$p is purged of its toxins.", ch, obj, NULL, TO_CHAR );
		}
		else
		{
			act( "$p did not seem to be affected.", ch, obj, NULL, TO_CHAR );
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_nightmare)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_nightmare(%p,%p,%p,%p)",sn,level,ch,vo);

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

	if (get_apply(victim, APPLY_RES_EVIL) > 0)
	{
		act( "$N is warded from your evil visions!", ch, NULL, victim, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (victim->position != POS_SLEEPING)
	{
		act( "$N cannot receive your dream.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}

	af.type      = sn;
	af.duration  = level;
	af.bitvector = 0;
	af.bittype   = AFFECT_TO_NONE;
	af.location  = APPLY_SAVES;
	af.modifier  = 0 - 2;
	af.level     = level;
	affect_to_char( ch, victim, &af );
	
	update_pos(victim, POS_SITTING);

	act( "{118}A nightmarish apparition invades your dream!", ch, NULL, victim, TO_VICT );
	act( "You project your nightmare into $N's mind.", ch, NULL, victim, TO_CHAR );
	act( "{118}$n awakes with a bloodcurdling scream", victim, NULL, NULL, TO_ROOM );

	damage( ch, victim, spell_dice(ch, sn, 1, 10), sn, NULL );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_null)
{
	push_call("spell_null(%p,%p,%p,%p)",sn,level,ch,vo);

	send_to_char( "We haven't done that one yet!\n\r", ch );
	pop_call();
	return FALSE;
}

/*
 * Permanency spell made easy -
 * Simply change duration on current
 * effect to -1, no longer expires,
 * but does get purged on death. - Kregor
 */
DO_SPELL(spell_permanency)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	int paf_type, cost;

	push_call("spell_permanency(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (*target_name == '\0')
	{
		act("Make what spell permanent?", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if ((paf_type = skill_lookup(target_name)) == -1 || !is_spell(paf_type))
	{
		act("That is not a valid spell.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!is_affected(victim, paf_type))
	{
		act("That spell is not currently active.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!IS_SET(skill_table[paf_type].flags, SF_PERMANENCY))
	{
		act("$t cannot be made permanent.", ch, skill_table[paf_type].name, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	// must be 8 levels higher than the spell circle
	if (skill_table[paf_type].native_level + 8 > level)
	{
		act("You are not powerful enough to make $t permanent.", ch, skill_table[paf_type].name, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	//material cost = 2500gp per spell circle
	if ((cost = skill_table[paf_type].native_level * 250000) > ch->gold)
	{
		act("You need $t to make $T permanent.", ch, format_coins(cost, FALSE), skill_table[paf_type].name, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for ( paf = victim->first_affect; paf ; paf = paf_next )
	{
		paf_next = paf->next;
		
		if (paf->type == paf_type)
		{
			affect_modify(victim, paf, FALSE);
			
			paf->duration = -2;
			paf->level		= level;
			if (!IS_NPC(ch))
				paf->caster			= STRALLOC(ch->name);
			else
				paf->caster     = STRALLOC(format("m%d", ch->pIndexData->vnum));

			affect_modify(victim, paf, TRUE);
		}
	}
	
	gold_transaction(ch, 0 - cost);
	
	act("You expended $t and made $T permanent.", ch, format_coins(cost, FALSE), skill_table[paf_type].name, TO_CHAR);
		
	pop_call();
	return TRUE;
}


/*
 * Used for both phantasmal killer and wierd, based on spell flag - Kregor
 */
DO_SPELL(spell_phantasmal_killer)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch;
	CHAR_DATA *vch_next;
	int save;

	push_call("spell_phantasmal_killer)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

// 		if (!can_mass_cast(ch, vch, sn))
// 			continue;
// 		
		if (sn == gsn_phantasmal_killer && vch != victim)
			continue;

		if (!is_same_group(victim, vch))
			continue;
		
		if (will_save(vch, ch, level, sn))
		{
			act("{138}$N scoffs at your illusion!", ch, NULL, vch, TO_CHAR);
			act("{138}You see through the illusion $n has put before you.", ch, NULL, vch, TO_VICT);
			continue;
		}
		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
		{
			continue;
		}
		else
		{
			act("{118}$N screams in terror!", ch, NULL, vch, TO_CHAR);
			act("{118}You scream in terror as your worst nightmare devours you!", ch, NULL, vch, TO_VICT);
			act("{118}$N screams in terror at something only $E can see!", ch, NULL, vch, TO_NOTVICT);

			if (save == PARTIAL)
			{
				damage( ch, vch, dice(3,6), sn, NULL );

				if (IS_AWAKE(vch))
				{
					AFFECT_DATA af;
		
					af.type      = sn;
					af.location  = APPLY_NONE;
					af.modifier  = 0;
					af.level     = level;
					af.duration  = 1;
					af.bittype   = AFFECT_TO_CHAR;
					af.bitvector = AFF2_DAZED;
					affect_join( ch, vch, &af );
				}

				continue;
			}
			else
			{
				damage( ch, vch, vch->hit + 10, sn, NULL );
				continue;
			}
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_poison)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_poison(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_AFFECTED(victim, AFF_POISON))
	{
		act( "$N is already poisoned.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}

	af.type      = sn;
	af.duration  = 6;
	af.location  = APPLY_NONE;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF_POISON;
	af.level     = level;

	affect_to_char( ch,victim, &af );

	stat_damage(ch, victim, APPLY_CON_DAMAGE, gsn_poison);

	act( "{128}You feel poison coursing through your veins.", victim, NULL, NULL, TO_CHAR );
	act( "{128}$N shivers and suffers as if poisoned!", victim, NULL, NULL, TO_ROOM );

	pop_call();
	return TRUE;
}

/*
 * Unified SPELL_FUN for all polymorph
 * spells, filtering thru the morphing
 * function to do the magic; this just
 * decides who gets funneled thru it - Kregor
 */
DO_SPELL(spell_polymorph)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	char buf1[MAX_INPUT_LENGTH];
	char buf2[MAX_INPUT_LENGTH];
	int cnt, save, race, hitdice, maxdice;
	bool area = FALSE;

	push_call("spell_polymorph(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!is_string(target_name))
	{
		ch_printf_color(ch, "Transmute into what?\n\r");
		pop_call();
		return FALSE;
	}
	
	two_arguments(target_name, buf1, buf2);

	if (is_polymorph(ch))
	{
		if (victim == ch)
			ch_printf_color(ch, "You are already in an alternate form.");
		else
			act("$N is already in an alternate form.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (target != TAR_CHAR_OFFENSIVE && !is_same_group(ch, victim))
	{
		act("$N is not a willing target ($e must be grouped).", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if ((race = lookup_race(target_name)) == -1)
	{
		if ((race = lookup_race(buf2)) == -1)
		{
			if ((race = lookup_race(buf1)) == -1)
			{
				act("That is not a valid race.", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return FALSE;
			}
		}
	}
	
	maxdice = level + polymorph_synergy(ch, race_table[race].type);
	
	if ((hitdice = race_table[race].hit_dice) > maxdice)
	{
		act("That form is too powerful for you.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (IS_SET(race_table[race].flags, RSPEC_INCORPOREAL))
	{
		act("That race is not possible with this spell.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (sn == gsn_alter_self)
	{
		if (race_table[race].type != race_table[ch->race].type)
		{
			act("You can only assume a form that is the same racial type.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (race_table[race].size < SIZE_SMALL || race_table[race].size > SIZE_MEDIUM)
		{
			act("You can only assume the form of a small or medium creature.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (hitdice > race_table[ch->race].hit_dice)
		{
			act("That creature is too strong.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	}

	if (sn == gsn_beast_shape)
	{
		if (race_table[race].type != RTYPE_ANIMAL)
		{
			act("That race is not an animal.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (race_table[race].size < race_table[ch->race].size - 1)
		{
			act("That creature is too small.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (race_table[race].size > race_table[ch->race].size + 1)
		{
			act("That creature is too big.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	}

	if (sn == gsn_polymorph)
	{
		if (race_table[race].size < race_table[ch->race].size - 2)
		{
			act("That creature is too small.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (race_table[race].size > race_table[ch->race].size + 2)
		{
			act("That creature is too big.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	}

	if (sn == gsn_baleful_polymorph)
	{
		if (hitdice > 1)
		{
			act("That creature is too strong.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		if (race_table[race].type != RTYPE_ANIMAL)
		{
			act("That race is not an animal.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	}
	
	if (sn == gsn_animal_shapes && race_table[race].type != RTYPE_ANIMAL)
	{
		act("That is not an animal.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}	

	if (sn != gsn_shapechange)
	{
		if (hitdice > 15)
		{
			act("That creature is too strong.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
		switch (race_table[race].type)
		{
			case RTYPE_UNDEAD:
			case RTYPE_OUTSIDER:
			case RTYPE_CONSTRUCT:
			case RTYPE_OOZE:
				act("That race type is not possible with this spell.", ch, NULL, NULL, TO_CHAR);
				pop_call();
				return FALSE;
			case RTYPE_DRAGON:
			case RTYPE_PLANT:
			case RTYPE_ABERRATION:
				if (sn != gsn_greater_polymorph)
				{
					act("That race type is not possible with this spell.", ch, NULL, NULL, TO_CHAR);
					pop_call();
					return FALSE;
				}
				break;
			default:
				break;
		}
	}
			
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (IS_AREA_SPELL(sn))
		area = TRUE;
		
	for (cnt = 0, vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (!area && vch != victim)
			continue;
			
		if (cnt && cnt >= level)
			break;
			
		// willing target = in same group as caster - Kregor
		if (target == TAR_CHAR_OFFENSIVE)
		{
			if (!can_mass_cast(ch, vch, sn))
				continue;
		}
		else if (!is_same_group(ch, vch))
			continue;
		
		if (is_polymorph(vch))
		{
			act("$N is already in an alternate form.", ch, NULL, vch, TO_CHAR);
			continue;
		}
			
		if (race == get_race(vch))
			continue;

		if (vch->level < race_table[race].hit_dice)
		{
			act("$N is too weak to assume that form.", ch, NULL, vch, TO_CHAR);
			continue;
		}

		if ((save = save_resist(ch, vch, sn, level)) != FALSE)
			continue;

		if (!morph(ch, vch, race, sn, level))
			continue;
			
		cnt++;

		if (!area)
			break;
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_power_word_blind)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_power_word_blind(%p,%p,%p,%p)",sn,level,ch,vo);

	if (victim == ch)
	{
		send_to_char_color("You shouldn't shout at yourself that way...", ch);
		pop_call();
		return FALSE;
	}
	
	if (IS_AFFECTED(victim, AFF_BLIND))
	{
		act( "$N is already blinded.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if ( victim->hit > 200 )
	{
		act( "{078}You bark out a single word, and nothing happens...", ch, NULL, victim, TO_CHAR );
		act( "{078}$n barks out a single word, and nothing happens...", ch, NULL, victim, TO_VICT );
		act( "{078}$n barks out a single word, and nothing happens...", ch, NULL, victim, TO_NOTVICT );
		pop_call();
		return TRUE;
	}

	af.type      = sn;
	af.location  = APPLY_NONE;
	af.modifier  = 0;
	if ( victim->hit <= 50 )
		af.duration  = -1;
	else if ( victim->hit <= 100 )
		af.duration  = spell_dice(ch, sn, 1, 4) + 1 * turn;
	else if ( victim->hit <= 200 )
		af.duration  = spell_dice(ch, sn, 1, 4) + 1;
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF_BLIND;
	af.level     = level;
	affect_to_char( ch, victim, &af );

	act( "{138}You bark out a single word, blinding $N!", ch, NULL, victim, TO_CHAR );
	act( "{138}$n barks out a single word, blinding you!", ch, NULL, victim, TO_VICT );
	act( "{138}$n barks out a single word, blinding $N!", ch, NULL, victim, TO_NOTVICT );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_power_word_stun)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_power_word_stun(%p,%p,%p,%p)",sn,level,ch,vo);

	if (victim == ch)
	{
		send_to_char_color("You shouldn't shout at yourself that way...", ch);
		pop_call();
		return FALSE;
	}
	
	if ( IS_AFFECTED(victim, AFF2_STUNNED) )
	{
		act( "$N is already srunned.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if ( victim->hit > 150 )
	{
		act( "{078}You bark out a single word, and nothing happens...", ch, NULL, victim, TO_CHAR );
		act( "{078}$n barks out a single word, and nothing happens...", ch, NULL, victim, TO_VICT );
		act( "{078}$n barks out a single word, and nothing happens...", ch, NULL, victim, TO_NOTVICT );
		pop_call();
		return TRUE;
	}

	af.type      = sn;
	af.location  = APPLY_NONE;
	af.modifier  = 0;
	if ( victim->hit <= 50 )
		af.duration  = spell_dice(ch, sn, 4, 4);
	else if ( victim->hit <= 100 )
		af.duration  = spell_dice(ch, sn, 2, 4);
	else if ( victim->hit <= 150 )
		af.duration  = spell_dice(ch, sn, 1, 4);
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF2_STUNNED;
	af.level     = level;
	affect_to_char( ch, victim, &af );

	act( "{138}You bark out a single word, leaving $N stunned!", ch, NULL, victim, TO_CHAR );
	act( "{138}$n barks out a single word, leaving you stunned!", ch, NULL, victim, TO_VICT );
	act( "{138}$n barks out a single word, leaving $N stunned!", ch, NULL, victim, TO_NOTVICT );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_power_word_kill)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam = get_max_hit(victim) + 11;

	push_call("spell_power_word_kill(%p,%p,%p,%p)",sn,level,ch,vo);

	if (victim == ch)
	{
		send_to_char_color("You shouldn't shout at yourself that way...", ch);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if ( victim->hit > 100 )
	{
		act( "{078}You bark out a single word, and nothing happens...", ch, NULL, victim, TO_CHAR );
		act( "{078}$n barks out a single word, and nothing happens...", ch, NULL, victim, TO_VICT );
		act( "{078}$n barks out a single word, and nothing happens...", ch, NULL, victim, TO_NOTVICT );
		pop_call();
		return TRUE;
	}

	act( "{138}You bark out a single word, which tears the soul from $N!", ch, NULL, victim, TO_CHAR );
	act( "{138}$n barks out a single word, which tears the soul from you!", ch, NULL, victim, TO_VICT );
	act( "{138}$n barks out a single word, which tears the soul from $N!", ch, NULL, victim, TO_NOTVICT );

	damage(ch, victim, dam, sn, NULL);
	pop_call();
	return TRUE;
}


DO_SPELL(spell_produce_flame)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	OBJ_DATA *fire;

	push_call("spell_produce_flame(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (target == TAR_CHAR_OFFENSIVE)
	{
		int dam = spell_dice(ch, sn, 1, 6) + UMIN(level, 5);

		act("{118}You hurl a ball of flame at $N.", ch, NULL, victim, TO_CHAR);
		act("{118}$n hurls a ball of flame at $N.", ch, NULL, victim, TO_NOTVICT);
		act("{118}$n hurls a ball of flame at you.", ch, NULL, victim, TO_VICT);
		spell_damage(ch, victim, dam, sn, level);
	}
	else if (target == TAR_OBJ_INV)
	{
		if (IS_OBJ_STAT(obj, ITEM_MAGIC))
		{
			act( "$p cannot be ignited.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}

		switch (obj->material)
		{
			default:
				send_to_char("That is not flammable.\n\r", ch);
				pop_call();
				return TRUE;
			case MATERIAL_HARDWOOD:
			case MATERIAL_SOFTWOOD:
			case MATERIAL_OAK:
			case MATERIAL_YEW:
			case MATERIAL_EBONY:
			case MATERIAL_PAPER:
			case MATERIAL_OIL:
				break;
		}

		if ((fire = create_object( get_obj_index( OBJ_VNUM_FIRE ), 0 )) == NULL)
		{
			bug("do_burn: OBJ_VNUM_FIRE not created.", 0);
			pop_call();
			return FALSE;
		}

		fire->value[2] = obj->size * obj->size;
		act( "You set fire to $p.", ch, obj, NULL, TO_CHAR );
		act( "$n sets fire to $p.", ch, obj, NULL, TO_ROOM );
		obj_to_room( fire, ch->in_room->vnum );
		junk_obj(obj);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_remove_disease)
{
	CHAR_DATA	*victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	DISEASE_DATA *dis, *dis_next;
	bool cured = FALSE;
	int caster_check;

	push_call("spell_remove_disease(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	caster_check = spell_dice(ch, sn, 1, 20) + level;
	
	if (target == TAR_CHAR_DEFENSIVE)
	{
		if (victim->first_disease != NULL)
		{
			for (dis = victim->first_disease ; dis ; dis = dis_next)
			{
				dis_next = dis->next;
				
				if (dis->dc && caster_check < dis->dc)
					continue;
				if (!dis->dc && caster_check < disease_table[dis->type].dc)
					continue;

				disease_from_char(victim, dis);
			}
			if (!victim->first_disease)
				cured = TRUE;
		}
		if ( cured )
		{
			act( "{138}You feel the wrackings of your sickness fade.", victim, NULL, NULL, TO_CHAR );
			act( "{138}$n begins to look healthier.", victim, NULL, NULL, TO_ROOM );
		}
		else
		{
			act("Your spell alleviated nothing.", ch, NULL, NULL, TO_CHAR);
		}
	}
	else if (target == TAR_OBJ_INV)
	{
		if (obj->first_disease != NULL)
		{
			for (dis = obj->first_disease ; dis ; dis = dis_next)
			{
				dis_next = dis->next;
				
				if (caster_check < disease_table[dis->type].dc)
					continue;

				disease_from_obj(obj, dis);
			}
			if (!obj->first_disease)
				cured = TRUE;
		}
		if ( cured )
		{
			act( "$p is cleansed of its disease.", ch, obj, NULL, TO_CHAR );
		}
		else
		{
			act( "$p did not seem to be affected.", ch, obj, NULL, TO_CHAR );
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_secure_shelter)
{
	char buf[MAX_INPUT_LENGTH];
	EXIT_DATA *pExit;
	ROOM_INDEX_DATA *pRoomIndex;
	int door, rip_door, vnum, range;

	push_call("spell_secure_shelter(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_NPC(ch))
	{
		pop_call();
		return FALSE;
	}

	if (!IS_SET(ch->in_room->room_flags, ROOM_RIP))
	{
		if (IS_SET(ch->in_room->room_flags,	ROOM_SAFE)
		||  IS_SET(ch->in_room->room_flags,	ROOM_NO_RIP)
		||  IS_SET(ch->in_room->area->flags,	AFLAG_NORIP)
		||  IS_SET(ch->in_room->room_flags,	ROOM_NO_RECALL)
		||  IS_SET(ch->in_room->area->flags,	AFLAG_NORECALL))
		{
			send_to_char("You are prevented from ripping space here!\n\r",ch);
			pop_call();
			return FALSE;
		}
	}
	else
	{
		send_to_char("You can't create a rip within another rip in space.\n\r",ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (door = range = 0 ; door < 6 ; door++)
	{
		if (ch->in_room->exit[door] == NULL)
		{
			range++;
		}
	}
	if (range == 0)
	{
		send_to_char("You failed to find space to create a rip here.\n\r", ch);
		pop_call();
		return FALSE;
	}
	vnum		= number_range(1, range);
	rip_door	= 0;

	for (door = 0 ; door < 6 ; door++)
	{
		if (ch->in_room->exit[door] == NULL)
		{
			if (--vnum == 0)
			{
				rip_door = door;
				break;
			}
		}
	}

	/* find valid vnum */

	for (vnum = ROOM_VNUM_RIFT ; vnum < MAX_VNUM && get_room_index(vnum) != NULL ; vnum++);

	if (vnum >= MAX_VNUM)
	{
		send_to_char("You are prevented from ripping up the Realm!\n\r",ch);
		pop_call();
		return FALSE;
	}

	create_room(vnum);
	pRoomIndex = room_index[vnum];
	pRoomIndex->description	= STRDUPE(str_empty);
	pRoomIndex->listen_desc	= STRDUPE(str_empty);
	pRoomIndex->night_desc	= STRDUPE(str_empty);

	sprintf(buf, "%s's Secure Shelter", ch->name);
	pRoomIndex->name			= STRALLOC(buf);
	pRoomIndex->area			= room_index[ROOM_VNUM_LIMBO]->area;
	pRoomIndex->vnum			= vnum;
	pRoomIndex->description		= STRALLOC("You see an area that defies all description.\n\r");
	pRoomIndex->room_flags		= ROOM_RIP|ROOM_NO_MOB|ROOM_INDOORS|ROOM_NO_RECALL|ROOM_NO_SAVE|ROOM_SAFE|ROOM_INN;
	pRoomIndex->sector_type		= SECT_INSIDE;
	pRoomIndex->creator_pvnum	= ch->pcdata->pvnum;

	for (door = 0 ; door < MAX_LAST_LEFT ; door++)
	{
		pRoomIndex->last_left[door] = STRDUPE(str_empty);
	}

	/* new room exit points to old room */

	create_exit(pRoomIndex, rev_dir[rip_door]);
	pExit	= pRoomIndex->exit[rev_dir[rip_door]];

	RESTRING(pExit->description, "You see an opening exiting the fabric of space and time.");
	pExit->to_room	= ch->in_room->vnum;

	create_exit(ch->in_room, rip_door);
	pExit	= ch->in_room->exit[rip_door];

	RESTRING(pExit->description,	"You see an opening in the fabric of space and time.");
	SET_BIT(pExit->exit_info, EX_RIP);
	pExit->pvnum		= ch->pcdata->pvnum;
	pExit->to_room		= pRoomIndex->vnum;

	act( "$n creates a room of haven $twards.", ch, dir_name[rip_door], NULL, TO_ROOM);
	ch_printf_color(ch, "You create a room of haven %swards.\n\r", dir_name[rip_door]);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_shatter)
{
	CHAR_DATA *victim = NULL;
	OBJ_DATA *obj = NULL;
	ROOM_INDEX_DATA *to_room;
	EXIT_DATA *pExit, *pExit_rev;
	int diceroll, dmg, dc;
	int door = -1;

	push_call("spell_shatter(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (is_string(target_name))
	{
		if ((victim = get_char_room(ch, target_name)) == NULL)
		{
			if ((obj = get_obj_room(ch, target_name)) == NULL)
			{
				if ((door = find_door(ch, target_name)) == -1)
				{
					act("You cannot find any $t here.", ch, target_name, NULL, TO_CHAR);
					pop_call();
					return FALSE;
				}
			}
		}
	}

	if (door != -1)
	{
		pExit = ch->in_room->exit[door];

		if (IS_SET(pExit->exit_info, EX_BASHED))
		{
			send_to_char( "It's already been bashed off its hinges!\n\r", ch );
			pop_call();
			return FALSE;
		}

		if (!IS_SET(pExit->exit_info, EX_CLOSED))
		{
			send_to_char( "It is already open.\n\r", ch );
			pop_call();
			return FALSE;
		}
		if (!IS_SET(pExit->exit_info, EX_BASHPROOF|EX_MAGICPROOF))
		{
			send_to_char( "It resists your spell.\n\r", ch );
			pop_call();
			return FALSE;
		}
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act("{178}A loud ringing noise pulses out from you!", ch, NULL, NULL, TO_CHAR);
	act("{178}A loud ringing noise pulses out from $n!", ch, NULL, NULL, TO_CHAR);

	// hacked bash code to work shatter against a door - Kregor
	if (door != -1)
	{
		pExit = ch->in_room->exit[door];

		diceroll = spell_dice(ch, sn, 1, 20) + level;
		
		if (IS_SET(pExit->exit_info, EX_IRON_DOOR))
			dc = 28;
		else if (IS_SET(pExit->exit_info, EX_HEAVY_DOOR))
			dc = 23;
		else if (IS_SET(pExit->exit_info, EX_WEAK_DOOR))
			dc = 13;
		else
			dc = 18;
		
		if (IS_SET(pExit->exit_info, EX_MAGICAL_LOCK))
			dc += 10;
		else if (IS_SET(pExit->exit_info, EX_BARRED))
			dc += 5;
		
		if (!IS_SET(pExit->exit_info, EX_BASHPROOF) && diceroll >= dc)
		{
			REMOVE_BIT(pExit->exit_info, EX_CLOSED);
			REMOVE_BIT(pExit->exit_info, EX_LOCKED);
			SET_BIT( pExit->exit_info, EX_BASHED );

			act( "$d shatters off its hinges!", ch, NULL, pExit->keyword, TO_CHAR );
			act( "$d shatters off its hinges!",  ch, NULL, pExit->keyword, TO_ROOM );

			if ((to_room = room_index[pExit->to_room]) != NULL
			&&  (pExit_rev = room_index[pExit->to_room]->exit[rev_dir[door]]) != NULL
			&&  room_index[pExit_rev->to_room] == ch->in_room )
			{
				CHAR_DATA *rch;

				REMOVE_BIT(pExit_rev->exit_info, EX_CLOSED);
				REMOVE_BIT(pExit_rev->exit_info, EX_LOCKED);
				SET_BIT( pExit_rev->exit_info, EX_BASHED );

				for (rch = to_room->first_person ; rch ; rch = rch->next_in_room)
				{
					act( "$d shatters off its hinges!", rch, NULL, pExit_rev->keyword, TO_CHAR);
				}
			}
		}
		else
		{
			act( "$d holds firm against your sundering.", ch, NULL, pExit->keyword, TO_CHAR );
			act( "Nothing seems to happen....", ch, NULL, NULL, TO_ROOM );
		}
	}
	else if (victim)
	{
		switch (material_table[race_table[get_race(victim)].material].parent)
		{
			case MATERIAL_TYPE_GEM:
			case MATERIAL_TYPE_CRYSTAL:
				spell_damage( ch, victim, spell_dice(ch, sn, UMIN(level,10), 6), sn, level );
				break;
			case MATERIAL_TYPE_ROCK:
			case MATERIAL_TYPE_METAL:
				spell_damage( ch, victim, spell_dice(ch, sn, 2, 6) + UMIN(level, 10), sn, level );
				break;
			default:
				act("Your spell has no effect on $N.", ch, NULL, victim, TO_CHAR);
				act("Nothing seems to happen....", ch, NULL, NULL, TO_ROOM);
				break;
		}
	}
	else if (obj)
	{
		if (IS_OBJ_STAT(obj, ITEM_MAGIC))
		{
			act("$p is protected by its magic aura.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return TRUE;
		}
		if (IS_OBJ_STAT(obj, ITEM_BROKEN))
		{
			act("$p is already broken!", ch, obj, NULL, TO_CHAR);
			pop_call();
			return TRUE;
		}
		if (IS_OBJ_STAT(obj, ITEM_BURIED))
		{
			act("$p would have to be dug up first!", ch, obj, NULL, TO_CHAR);
			pop_call();
			return TRUE;
		}
		if (IS_OBJ_STAT(obj, ITEM_INVENTORY))
		{
			act("$p resists your spell.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return TRUE;
		}
		// 10 lbs / caster level limit
		if (obj->weight > 100 * level)
		{
			act("$p is too heavy for you to affect.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return TRUE;
		}
		dmg = spell_dice(ch,sn,2,6) + UMIN(level, 10);
		dmg = obj_damage_modify(obj, NULL, dmg, DAM_SONIC);
		obj->hit_points -= dmg;
		if (obj->hit_points <= 0)
		{
			act( "$p shatters into pieces!", ch, obj, NULL, TO_ALL);
			junk_obj(obj);
		}
		else if (dmg)
		{
			act( "$p is damaged.", ch, obj, NULL, TO_ALL);
		}
		else
		{
			act( "$p is unaffected.", ch, obj, NULL, TO_ALL);
		}
	}
	// None of the above means it was non-targeted
	else
	{
		for (obj = ch->in_room->first_content ; obj ; obj = obj->next_content)
		{
			if (IS_OBJ_STAT(obj, ITEM_MAGIC|ITEM_BROKEN|ITEM_INVENTORY|ITEM_BURIED))
			{
				continue;
			}
			if (obj->weight > 10 * level)
			{
				continue;
			}
			if (material_table[obj->material].parent != MATERIAL_TYPE_CRYSTAL)
			{
				continue;
			}
			dmg = spell_dice(ch,sn,2,6) + UMIN(level, 10);
			dmg = obj_damage_modify(obj, NULL, dmg, DAM_SONIC);
			obj->hit_points -= dmg;
			if (obj->hit_points <= 0)
			{
				act( "$p shatters into pieces!", ch, obj, NULL, TO_ALL);
				junk_obj(obj);
			}
			else if (dmg)
			{
				act( "$p is damaged.", ch, obj, NULL, TO_ALL);
			}
			else
			{
				act( "$p is undaffected.", ch, obj, NULL, TO_ALL);
			}
		}
	}
	
	pop_call();
	return TRUE;
}

DO_SPELL(spell_shadow_bolt)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;

	push_call("spell_shadow_bolt(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act( "{108}A bolt of darkness shoots from $n's finger.", ch, NULL, victim, TO_ROOM );
	act( "{108}A bolt of darkness shoots from your finger.", ch, NULL, victim, TO_CHAR );

	damage(ch, victim, spell_dice(ch, sn, 1,8)+1, sn, NULL );

	if (valid_victim(victim) && !save_resist(ch, victim, sn, level))
	{
		AFFECT_DATA af;

		af.type      = sn;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.level     = level;
		af.duration  = 1;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_DAZED;
		affect_join( ch, victim, &af );
	}

	pop_call();
	return TRUE;
}


DO_SPELL(spell_sign_of_wrath)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch;
	int dam, chroll, vchroll;

	push_call("spell_sign_of_wrath(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	dam = spell_dice(ch, sn, UMIN(level, 15), 6);
	
	act( "{138}A powerful symbol of wrath pulses around $n!", ch, NULL, NULL, TO_ROOM);
	act( "{138}A wave of force burts out around you.", ch, NULL, NULL, TO_CHAR);

	spell_damage(ch, victim, dam, sn, level);

	chroll = dice(1,20) + level;
	
	for (vch = victim->in_room->first_person ; vch ; vch = vch->next_in_room)
	{
		if (!can_mass_cast(ch, vch, sn))
			continue;

		vchroll = dice(1,20) + combat_maneuver_bonus(vch) + stat_bonus(TRUE, vch, APPLY_DEX);
		
		if (quadruped(vch))
			vchroll += 4;
		if (many_legged(vch))
			vchroll += 8;
		if (race_skill(vch, gsn_stability))
			vchroll += 4;
		
		if (chroll < vchroll)
			continue;

		act( "$n is knocked back by the force of the symbol!", vch, NULL, NULL, TO_ROOM );
		act( "You are knocked over by the force of the symbol!", vch, NULL, NULL, TO_CHAR );
		
		update_pos(vch, POS_RESTING);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_simulacrum)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *mh;
	char illusionname[MAX_INPUT_LENGTH];
	int i, cls, skill;

	push_call("spell_simulacrum(%p,%p,%p,%p)",sn,level,ch,vo);

	if (victim->level > ch->level * 2)
	{
		act("$N is too powerful for you to copy.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	mh = create_mobile(get_mob_index(MOB_VNUM_ILLUSION));
	char_to_room( mh, ch->in_room->vnum, TRUE );

	mh->level	= victim->level/2;
	mh->class = victim->class;
	for (cls = CLASS_MONSTER ; cls < MAX_CLASS ; cls++)
	{
		if (!victim->mclass[cls])
			mh->mclass[cls] = 0;
		else
			mh->mclass[cls] = UMAX(1, victim->mclass[cls] / 2);
	}
	for (skill = 0 ; skill < MAX_REAL_SKILL ; skill++)
	{
		if (skill_table[skill].spell_school != FSKILL_SKILL
		&& skill_table[skill].spell_school != FSKILL_FEAT)
			continue;
		if (victim->learned[skill] == 0)
			continue;
		mh->learned[skill] = UMAX(1, victim->learned[skill]);
	}
	
	mh->npcdata->sac_timer = level * hr;
	mh->npcdata->sac_string = STRALLOC("$n shimmers then winks out of existence.");
	mh->race     = victim->race;
	mh->sex      = victim->sex;
	mh->perm_str = victim->perm_str;
	mh->perm_dex = victim->perm_dex;
	mh->perm_con = victim->perm_con;
	mh->perm_int = victim->perm_int;
	mh->perm_wis = victim->perm_wis;
	mh->perm_cha = victim->perm_cha;
	mh->max_hit  = UMAX(1, victim->max_hit/2);
	mh->hit      = get_max_hit(mh);

	for (illusionname[0] = '\0', i = 0 ; victim->name[i] != '\0' ; i++)
	{
		if (victim->name[i] == ' ')
		{
			strcat(illusionname, "^ ");
		}
		else
		{
			cat_sprintf(illusionname, "%c", victim->name[i]);
		}
	}
	strcat(illusionname, "^");

	RESTRING(mh->name, illusionname);
	if (IS_NPC(victim))
	{
		RESTRING(mh->short_descr, victim->short_descr);
	}
	else
	{
		RESTRING(mh->short_descr, victim->name);
	}
	if (IS_NPC(victim))
	{
		RESTRING(mh->long_descr, victim->long_descr);
	}
	else
	{
		RESTRING(mh->long_descr, victim->long_descr);
	}
	RESTRING(mh->description, victim->description);

	SET_BIT( mh->affected_by , AFF_DOMINATE );
	act( "A duplicate of $N takes shape before your eyes.", ch, NULL, victim, TO_ALL);
	add_follower( mh, ch );
	pop_call();
	return TRUE;
}


DO_SPELL(spell_sirocco)
{
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int rtd_type = 0;
	int dam, save;
	ROOM_TIMER_DATA *rtd, *rtd_next;

	push_call("spell_sirocco(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act("{038}You whip up a blinding sandstorm!", ch, NULL, NULL, TO_CHAR);
	act("{038}$n whips up a blinding sandstorm!", ch, NULL, NULL, TO_ROOM);
	
	for (vch = ch->in_room->first_person ; vch ; vch = vch_next)
	{
		vch_next = vch->next_in_room;

		//strip insect plage from victims with the gust
		if (is_affected(vch, gsn_insect_plague))
		{
			act("{038}The insects swarming around you scatter in the winds!", vch, NULL, NULL, TO_CHAR);
			act("{038}The insects swarming around $n scatter in the winds!", vch, NULL, NULL, TO_ROOM);
			affect_strip(vch, gsn_insect_plague);
		}

		if (vch == ch)
			continue;
			
		if (!can_mass_cast(ch, vch, sn))
			continue;

		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
			continue;

		switch(get_size(vch))
		{
			case SIZE_FINE:
			case SIZE_DIMINUTIVE:
			case SIZE_TINY:
				if (IS_FLYING(vch))
				{
					act("$n is buffeted back by the winds!", vch, NULL, NULL, TO_ROOM);
					act("{118}You are buffeted back by the winds!", vch, NULL, NULL, TO_CHAR);
					vch->distracted = 2;
					damage(ch, vch, dice(2,6), sn, NULL);
				}
				else
				{
					act("$n is bowled over by the winds!", vch, NULL, NULL, TO_ROOM);
					act("{118}You are bowled over by the winds!", vch, NULL, NULL, TO_CHAR);
					vch->distracted = 2;
					update_pos(vch, POS_RESTING);
					damage(ch, vch, dice(1,4), sn, NULL);
				}
				break;
			case SIZE_SMALL:
				if (IS_FLYING(vch))
				{
					if (str_roll(vch) < level / 2 + 10)
					{
						if (!domain_apotheosis(vch, DOMAIN_STRENGTH) || str_roll(vch) < level / 2 + 10)
						{
							act("$n is buffeted back by the winds!", vch, NULL, NULL, TO_ROOM);
							act("{118}You are buffeted back by the winds!", vch, NULL, NULL, TO_CHAR);
							vch->distracted = 2;
							damage(ch, vch, dice(1,6), sn, NULL);
						}
					}
				}
				else
				{
					act("$n is bowled over by the winds!", vch, NULL, NULL, TO_ROOM);
					act("{118}You are bowled over by the winds!", vch, NULL, NULL, TO_CHAR);
					vch->distracted = 2;
					update_pos(vch, POS_RESTING);
				}
				break;
			case SIZE_MEDIUM:
				if (!IS_FLYING(vch))
				{
					if (str_roll(vch) < level / 2 + 10)
					{
						if (!domain_apotheosis(vch, DOMAIN_STRENGTH) || str_roll(vch) < level / 2 + 10)
						{
							act("$n is bowled over by the winds!", vch, NULL, NULL, TO_ROOM);
							act("{118}You are bowled over by the winds!", vch, NULL, NULL, TO_CHAR);
							vch->distracted = 2;
							update_pos(vch, POS_RESTING);
						}
					}
				}
				break;
			default:
				break;
		}
		
		dam = level + spell_dice(ch, sn, 3, 6);

		if (save == PARTIAL)
			dam /= 2;
			
		damage(ch, vch, dam, sn, NULL);
		
		if (save == PARTIAL)
			continue;

		af.type      = sn;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.duration  = level / 2 + spell_dice(ch, sn, 1, 4);
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF_BLIND;
		af.level     = level;
		affect_to_char( ch, vch, &af );			
	}
	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next)
	{
		rtd_next = rtd->next;
		
		if (rtd->vnum == ch->in_room->vnum && IS_SET(rtd->bitvector, ROOM_FOG))
		{
			rtd->duration = 1;
			rtd_type = rtd->type;
		}
		if (rtd->vnum == ch->in_room->vnum && IS_SET(rtd->bitvector, ROOM_SWARM))
		{
			rtd->duration = 0;
		}
	}
	if (rtd_type != 0)
	{
		act("{168}The clouds are blown back by the winds!", ch, NULL, NULL, TO_ALL);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_protection_energy)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	int resist;

	push_call("spell_protection_energy(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	resist = UMIN(level * 12, 120);

	af.type = sn;
	af.duration	 = turn * level;
	af.bittype 	 = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;

	af.location	 = APPLY_IMM_ELECTRIC;
	af.modifier	 = resist;
	victim->absorption[APPLY_IMM_ELECTRIC] = resist;
	affect_join( ch, victim, &af );

	af.location	 = APPLY_IMM_ACID;
	af.modifier	 = resist;
	victim->absorption[APPLY_IMM_ACID] = resist;
	affect_join( ch, victim, &af );

	af.location	 = APPLY_IMM_COLD;
	af.modifier	 = resist;
	victim->absorption[APPLY_IMM_COLD] = resist;
	affect_join( ch, victim, &af );

	af.location	 = APPLY_IMM_FIRE;
	af.modifier	 = resist;
	victim->absorption[APPLY_IMM_FIRE] = resist;
	affect_join( ch, victim, &af );

	af.location	 = APPLY_IMM_SONIC;
	af.modifier	 = resist;
	victim->absorption[APPLY_IMM_SONIC] = resist;
	affect_join( ch, victim, &af );

	act( "{138}A powerful ward against the elements surrounds $n's body.", victim, NULL, NULL, TO_ROOM );
	act( "{138}A powerful ward against the elements surrounds your body.", victim, NULL, NULL, TO_CHAR );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_purify)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;

	push_call("spell_purify(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	switch (obj->item_type)
	{
		case ITEM_FOOD:
		case ITEM_DRINK_CON:
			if (obj->value[3] != 0)
			{
				act( "$p is purified.", ch, obj, NULL, TO_CHAR);
				act( "$p is purified.", ch, obj, NULL, TO_ROOM);
				obj->value[3] = 0;
			}
			break;
		case ITEM_FOUNTAIN:
			if (obj->value[3] != 0)
			{
				act( "$p runs clear once more.", ch, obj, NULL, TO_CHAR);
				act( "$p runs clear once more.", ch, obj, NULL, TO_ROOM);
				obj->value[3] = 0;
			}
			break;
		default:
			act( "$p is not edible.", ch, obj, NULL, TO_CHAR);
			pop_call();
			return FALSE;
	}

	if (obj->poison != NULL)
	{
		POISON_DATA *pd;
		
		while ((pd = obj->poison) != NULL)
		{
			obj->poison = obj->poison->next;
			FREEMEM(pd);
		}
		act( "$p is purified.", ch, obj, NULL, TO_CHAR);
		act( "$p is purified.", ch, obj, NULL, TO_ROOM);
	}		

	pop_call();
	return TRUE;
}


DO_SPELL(spell_death_knell)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;

	push_call("spell_harm(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (victim->hit > -1)
	{
		act( "Your spell has no effect.", victim, NULL, NULL, TO_CHAR);
	}
	else
	{
		spell_damage( ch, victim, victim->hit + 10, sn, level );
		
		if (victim->position == POS_DEAD)
		{
			AFFECT_DATA af;
			
			af.type = sn;
			af.duration	 = 5 * turn * victim->level;
			af.bittype 	 = AFFECT_TO_NONE;
			af.bitvector = 0;
			af.level     = level;
		
			af.location	 = APPLY_STR;
			af.modifier	 = 2;
			affect_join( ch, victim, &af );
		
			af.location	 = APPLY_HIT;
			af.modifier	 = dice(1,8);
			affect_join( ch, victim, &af );

			act("You draw power from the dying soul of $N.", ch, NULL, victim, TO_CHAR);
			act("$n draws power from your dying soul of $N.", ch, NULL, victim, TO_NOTVICT);
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_slay_living)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam, save;

	push_call("spell_slay_living(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	dam = level * 10;

	if (!IS_LIVING(victim))
	{
		act( "Your spell has no effect.", victim, NULL, NULL, TO_CHAR);
	}
	else if ((save = save_resist(ch, victim, sn, level)) == PARTIAL)
	{
		dam = spell_dice(ch, sn, 3, 6) + level;
	}
	else if (save)
	{
		pop_call();
		return TRUE;
	}
	damage(ch, victim, dam, sn, NULL);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_destruction)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam, save;

	push_call("spell_destruction(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	dam = 10 * level;

	if (!IS_LIVING(victim))
	{
		act( "Your spell has no effect.", victim, NULL, NULL, TO_CHAR);
	}
	else if ((save = save_resist(ch, victim, sn, level)) == PARTIAL)
	{
		dam = spell_dice(ch, sn, 3, 6) + level;
	}
	else if (save)
	{
		pop_call();
		return TRUE;
	}
	damage(ch, victim, dam, sn, NULL);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_disintegrate)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam, save;

	push_call("spell_disintegrate(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	dam = spell_dice(ch, sn, UMIN(2*level,40), 6);

	act("A green ray shoots from your finger at $N.", ch, NULL, victim, TO_CHAR);
	act("A green ray shoots from $n's finger at $N.", ch, NULL, victim, TO_NOTVICT);
	act("A green ray shoots from $n finger at you.", ch, NULL, victim, TO_VICT);

	if ((save = save_resist(ch, victim, sn, level)) == PARTIAL)
	{
		dam = spell_dice(ch, sn, 5, 6);
	}
	else if (save)
	{
		pop_call();
		return TRUE;
	}
	damage(ch, victim, dam, sn, NULL);

	pop_call();
	return TRUE;
}


/*
 * Low-end raising spell, requires corpse,
 * costs 1 con, suffers stat damage
 * and only humanoids can be raised - Kregor
 */
DO_SPELL (spell_raise_dead)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	OBJ_DATA *obj_content;
	CHAR_DATA *victim;
	AFFECT_DATA af;

	push_call("spell_raise_dead(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!obj)
	{
		send_to_char("You can't find a corpse here.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!IS_OBJ_TYPE(obj, ITEM_CORPSE_PC))
	{
		send_to_char("That is not a player corpse.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (IS_SET(obj->extra_flags, ITEM_NO_RAISE))
	{
		send_to_char("You will need a stronger prayer to return that spirit.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if ((victim = get_char_pvnum(obj->owned_by)) == NULL)
	{
		send_to_char("The spirit cannot be found.\n\r", ch);
		pop_call();
		return FALSE;
	}
	if (!IS_PLR(victim, PLR_DEAD))
	{
		send_to_char("The spirit has found a new vessel already!\n\r", ch);
		pop_call();
		return FALSE;
	}
	if (IS_PLR(victim, PLR_SOULBIND))
	{
		send_to_char("The spirit has been bound from the mortal plane.\n\r", ch);
		pop_call();
		return TRUE;
	}
	if (victim->perm_con <= 1)
	{
		send_to_char("That spirit is too weak to be raised.\n\r", ch);
		pop_call();
		return TRUE;
	}

	if (!IS_HUMANOID(victim))
	{
		send_to_char("Only a humanoid creature can be raised.\n\r", ch);
		pop_call();
		return FALSE;
	}

	/* cannot raise diametric aligned char */
	if (number_percent() < OPPOSITE_ALIGN(ch, victim))
	{
		act("{118}You faled to raise $N", ch, NULL, victim, TO_CHAR);
		act("{118}The gods failed to answer $n's prayer.", ch, NULL, victim, TO_ROOM);
		pop_call();
		return TRUE;
	}

	act("{138}$n's spirit departs from the beyond!", victim, NULL, NULL, TO_ROOM);
	char_from_room(victim);

	char_to_room(victim, ch->in_room->vnum, TRUE);

	destroy_mana(victim);
	victim->hit = victim->level;
	victim->move = victim->level;
	REMOVE_BIT(victim->act, PLR_DEAD);

	victim->perm_con--;
	
	af.type      = gsn_drain;
	af.duration  = -1;
	af.modifier  = 0 - UMIN(2, victim->perm_str - 1);
	af.location  = APPLY_STR_DRAIN;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = AFF_NONE;
	af.level		 = ch->level;	
	affect_join( ch, victim, &af );

	af.location  = APPLY_CON_DRAIN;
	af.modifier  = 0 - UMIN(2, victim->perm_con - 1);
	affect_join( ch, victim, &af );

	af.location  = APPLY_DEX_DRAIN;
	af.modifier  = 0 - UMIN(2, victim->perm_dex - 1);
	affect_join( ch, victim, &af );

	act("{138}You have been raised from the dead!", victim, NULL, NULL, TO_CHAR);
	act("{138}$n has been raised from the dead!", victim, NULL, NULL, TO_ROOM);

	while ((obj_content = obj->last_content) != NULL)
	{
		obj_from_obj(obj_content);
		obj_to_char(obj_content, victim);
	}
	extract_obj(obj);

	do_look(victim, "");

	pop_call();
	return TRUE;
}

/*
 * Druids can return the dead too,
 * you just have to come back in a
 * new body, and you take what you can get - Kregor
 */
DO_SPELL (spell_reincarnate)
{
	CHAR_DATA *victim;
	char arg[MAX_INPUT_LENGTH];
	int old_race, race;

	push_call("spell_reincarnate(%p,%p,%p,%p)",sn,level,ch,vo);

	one_argument(target_name, arg);
	
	if (arg[0] == '\0')
	{
		send_to_char("Whose spirit do you wish to reincarnate?\n\r", ch);
		pop_call();
		return FALSE;
	}
		
	if ((victim = get_player_world_even_blinded(arg)) == NULL)
	{
		send_to_char("The spirit cannot be found.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (!IS_PLR(victim, PLR_DEAD))
	{
		send_to_char("The spirit has found a new vessel already!\n\r", ch);
		pop_call();
		return FALSE;
	}
	if (IS_PLR(victim, PLR_SOULBIND))
	{
		send_to_char("The spirit has been bound from the mortal plane.\n\r", ch);
		pop_call();
		return TRUE;
	}
	
	act("{138}$n's spirit departs from the beyond!", victim, NULL, NULL, TO_ROOM);
	char_from_room(victim);

	char_to_room(victim, ch->in_room->vnum, TRUE);
	
	old_race = victim->race;
	
	switch(number_range(1,20))
	{
		case 1:
			race = RACE_BUGBEAR;
			break;
		case 2:
		case 3:
			race = RACE_DWARF;
			break;
		case 4:
		case 5:
			race = RACE_ELF;
			break;
		case 6:
			race = RACE_GNOLL;
			break;
		case 7:
		case 8:
			race = RACE_GNOME;
			break;
		case 9:
		case 10:
			race = RACE_HALFELF;
			break;
		case 11:
		case 12:
			race = RACE_HALFORC;
			break;
		case 13:
		case 14:
			race = RACE_HALFLING;
			break;
		case 15:
		case 16:
		case 17:
			race = RACE_HUMAN;
			break;
		case 18:
			race = RACE_PIXIE;
			break;
		case 19:
			race = RACE_CENTAUR;
			break;
		case 20:
			race = old_race;
			break;
	}
	
	victim->race = race;
	// new body = new physical stats
	victim->perm_str = 8 + dice(1,6) + race_table[race].race_mod[0];
	victim->perm_dex = 8 + dice(1,6) + race_table[race].race_mod[1];
	victim->perm_con = 8 + dice(1,6) + race_table[race].race_mod[2];

	RESTRING(victim->description, ""); // player has to write new description
	RESTRING(victim->pcdata->adjective, ""); // player has to set new adj
	REMOVE_BIT(victim->act, PLR_DEAD);

	victim->hit = get_max_hit(victim);
	victim->move = get_max_move(victim);
	restore_mana(victim);

	act("{138}Your spirit rewkakens in an unfamiliar body!", victim, NULL, NULL, TO_CHAR);
	act("{138}$n has been reincarnated as $t $T!", victim, a_an(race_table[race].race_name), race_table[race].race_name, TO_ROOM);
	do_look(victim, "");

	pop_call();
	return TRUE;
}


/*
 * Mid-end raising spell, requires corpse,
 * does not cause level loss - Kregor
 */
DO_SPELL (spell_resurrection)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	OBJ_DATA *obj_content;
	CHAR_DATA *victim;

	push_call("spell_resurrection(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!obj)
	{
		send_to_char("You can't find a corpse here.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!IS_OBJ_TYPE(obj, ITEM_CORPSE_PC))
	{
		send_to_char("That is not a player corpse.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if ((victim = get_char_pvnum(obj->owned_by)) == NULL)
	{
		send_to_char("The spirit cannot be found.\n\r", ch);
		pop_call();
		return FALSE;
	}
	if (!IS_PLR(victim, PLR_DEAD))
	{
		send_to_char("The spirit has found a new vessel already!\n\r", ch);
		pop_call();
		return FALSE;
	}
	if (IS_PLR(victim, PLR_SOULBIND))
	{
		send_to_char("The spirit has been bound from the mortal plane.\n\r", ch);
		pop_call();
		return TRUE;
	}
	if (victim->perm_con < 2)
	{
		send_to_char("Their spirit is too weak.\n\r", ch);
		pop_call();
		return TRUE;
	}

	/* cannot raise diametric aligned char */
	if (number_percent() < OPPOSITE_ALIGN(ch, victim))
	{
		act("{118}You faled to resurrect $N", ch, NULL, victim, TO_CHAR);
		act("{118}The gods failed to answer $n's prayer.", ch, NULL, victim, TO_ROOM);
		pop_call();
		return TRUE;
	}

	act("{138}$n's spirit departs from the beyond!", victim, NULL, NULL, TO_ROOM);
	char_from_room(victim);

	char_to_room(victim, ch->in_room->vnum, TRUE);

	victim->hit = get_max_hit(victim);
	victim->move = get_max_move(victim);
	restore_mana(victim);

	act("{138}You have been resurrected!", victim, NULL, NULL, TO_CHAR);
	act("{138}$n has been resurrected!", victim, NULL, NULL, TO_ROOM);
	REMOVE_BIT(victim->act, PLR_DEAD);

	while ((obj_content = obj->last_content) != NULL)
	{
		obj_from_obj(obj_content);
		obj_to_char(obj_content, victim);
	}
	extract_obj(obj);

	do_look(victim, "");

	pop_call();
	return TRUE;
}

/*
 * High-end raising spell, does not require
 * corpse, no limitations at all except for
 * alignment restriction - Kregor
 */
DO_SPELL (spell_true_resurrection)
{
	CHAR_DATA *victim;
	char arg[MAX_INPUT_LENGTH];

	push_call("spell_true_resurrection(%p,%p,%p,%p)",sn,level,ch,vo);

	one_argument(target_name, arg);
	
	if (arg[0] == '\0')
	{
		send_to_char("Whose spirit do you wish to resurrect?\n\r", ch);
		pop_call();
		return FALSE;
	}
		
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if ((victim = get_player_world_even_blinded(arg)) == NULL)
	{
		send_to_char("The spirit cannot be found.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!IS_PLR(victim, PLR_DEAD))
	{
		send_to_char("The spirit has found a new vessel already!\n\r", ch);
		pop_call();
		return FALSE;
	}
	
	/* cannot raise diametric aligned char */
	if (number_percent() < OPPOSITE_ALIGN(ch, victim))
	{
		act("{118}You faled to resurrect $N", ch, NULL, victim, TO_CHAR);
		act("{118}The gods failed to answer $n's prayer.", ch, NULL, victim, TO_ROOM);
		pop_call();
		return TRUE;
	}

	act("{138}$n's spirit departs from the beyond!", victim, NULL, NULL, TO_ROOM);
	char_from_room(victim);

	char_to_room(victim, ch->in_room->vnum, TRUE);

	victim->hit = get_max_hit(victim);
	victim->move = get_max_move(victim);
	restore_mana(victim);

	act("{138}You have been resurrected!", victim, NULL, NULL, TO_CHAR);
	act("{138}$n has been resurrected!", victim, NULL, NULL, TO_ROOM);
	REMOVE_BIT(victim->act, PLR_DEAD|PLR_SOULBIND);
	do_look(victim, "");

	pop_call();
	return TRUE;
}


/*
 * Binds soul from dead corpse to prevent raising/animation - Kregor
 */
DO_SPELL (spell_soul_bind)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	CHAR_DATA *victim;

	push_call("spell_soul_bind(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!obj)
	{
		send_to_char("You can't find a corpse here.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!IS_OBJ_TYPE(obj, ITEM_CORPSE_PC))
	{
		send_to_char("That is not a player corpse.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if ((victim = get_char_pvnum(obj->owned_by)) == NULL)
	{
		send_to_char("The spirit cannot be found to bind it.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!IS_PLR(victim, PLR_DEAD))
	{
		send_to_char("The spirit has found a new vessel already!\n\r", ch);
		pop_call();
		return TRUE;
	}

	if (will_save(victim, ch, level, sn))
	{
		act("$N's soul resists your attempt to bind it.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return TRUE;
	}

	SET_BIT(victim->act, PLR_SOULBIND);

	act("{108}You feel your soul being cut off from the mortal plane.", victim, NULL, NULL, TO_CHAR);
	act("{108}$n's soul has been bound from the mortal plane.", victim, NULL, NULL, TO_ROOM);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_refresh)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int move;

	push_call("spell_refresh(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	move = spell_dice(ch, sn, 2, 8) + UMIN(level, 10);
	
	victim->move = UMIN( victim->move + move, get_max_move(victim) );

	act( "{138}$n looks a bit more refreshed.", victim, NULL, NULL, TO_ROOM );
	act( "{138}You feel a bit more refreshed.", victim, NULL, NULL, TO_CHAR );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_remove_blindness)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;

	push_call("spell_remove_blindness(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!IS_AFFECTED(victim, AFF_BLIND))
	{
		act( "$N isn't blinded.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (IS_RACE_AFFECT(victim, AFF_BLIND))
	{
		act( "$N's blindness cannot be cured.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	AFFECT_STRIP( victim, AFF_BLIND );

	send_to_char( "Your vision returns!\n\r", victim );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_remove_deafness)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;

	push_call("spell_remove_deafness(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!IS_AFFECTED(victim, AFF_DEAF))
	{
		act( "$N isn't deafened.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	AFFECT_STRIP( victim, AFF_DEAF );

	send_to_char( "Your hearing returns!\n\r", victim );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_stone_to_flesh)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;

	push_call("spell_stone_to_flesh(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!IS_AFFECTED(victim, AFF2_PETRIFICATION))
	{
		act( "$N isn't petrified.", ch, NULL, victim, TO_CHAR );
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (!fort_save(victim, NULL, 15, -1))
	{
		act("{118}$n collapses under the shock of $s transformation!", victim, NULL, NULL, TO_ROOM);
		act("{118}You collapse under the shock of your transformation!", victim, NULL, NULL, TO_CHAR);
		damage(victim, victim, victim->hit + 11, TYPE_NOFIGHT, NULL);
		pop_call();
		return TRUE;
	}

	affect_strip( victim, gsn_flesh_to_stone );
	affect_strip( victim, gsn_stone_fist );
	AFFECT_STRIP( victim, AFF2_PETRIFICATION );	

	send_to_char( "Your stiffened joints can move once more!\n\r", victim );

	act( "$n's stony countenance reverts to flesh once more.", victim, NULL, NULL, TO_ROOM );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_stone_shower)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	int numdice, targets;
	int save;

	push_call("spell_stone_shower(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act( "{038}Stones rend from the ground and launch forward!", ch, NULL, NULL, TO_CHAR );
	act( "{038}Stones rend from the ground, spraying forth from $n", ch, NULL, NULL, TO_ROOM );
	
	targets = UMIN(level, 10);

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;

		if (!can_mass_cast(ch, vch, sn) || !is_same_group(victim, vch))
		{
			continue;
		}

		numdice = dice(1, targets);
		
		targets -= numdice;

		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
			continue;

		while (numdice)
		{
			damage( ch, vch, spell_dice(ch, sn, 1, 6) + 1, sn, NULL );
			--numdice;
		}
		
		if (targets <= 0)
			break;
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_storm_of_vengeance)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	ROOM_INDEX_DATA *room;
	ROOM_TIMER_DATA rtd;

	push_call("spell_storm_of_vengeance(%p,%p,%p,%p)",sn,level,ch,vo);

	if ((room = victim->in_room) == NULL)
	{
		pop_call();
		return FALSE;
	}
	
	if (!IS_OUTSIDE(victim))
	{
		send_to_char( "This storm can only take place outdoors.\n\r", ch );
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= 10;
	rtd.bitvector	= ROOM_FOG;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{108}Ominous clouds quickly gather in the area above $n.", victim, NULL, ch, TO_VICT);
	act( "{108}Ominous clouds gather and thunder rumbles!", victim, NULL, ch, TO_NOTVICT);
	act( "{108}Ominous clouds gather and thunder rumbles!", victim, NULL, NULL, TO_CHAR);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_resist_elements)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	int resist;

	push_call("spell_resist_elements(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (level >= 11)
		resist = 30;
	else if (level >= 7)
		resist = 20;
	else
		resist = 10;

	af.type = sn;
	af.duration	 = turn * level;
	af.bittype 	 = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;

	af.location	 = APPLY_DR_ELECTRIC;
	af.modifier	 = resist;
	victim->absorption[APPLY_DR_ELECTRIC] = 2 * resist;
	affect_join( ch, victim, &af );

	af.location	 = APPLY_DR_ACID;
	af.modifier	 = resist;
	victim->absorption[APPLY_DR_ACID] = 2 * resist;
	affect_join( ch, victim, &af );

	af.location	 = APPLY_DR_COLD;
	af.modifier	 = resist;
	victim->absorption[APPLY_DR_COLD] = 2 * resist;
	affect_join( ch, victim, &af );

	af.location	 = APPLY_DR_FIRE;
	af.modifier	 = resist;
	victim->absorption[APPLY_DR_FIRE] = 2 * resist;
	affect_join( ch, victim, &af );

	af.location	 = APPLY_DR_SONIC;
	af.modifier	 = resist;
	victim->absorption[APPLY_DR_SONIC] = 2 * resist;
	affect_join( ch, victim, &af );

	act( "{138}A ward against the elements surrounds $n's body.", victim, NULL, NULL, TO_ROOM );
	act( "{138}A ward against the elements surrounds your body.", victim, NULL, NULL, TO_CHAR );

	pop_call();
	return TRUE;
}


DO_SPELL(spell_restoration)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	int mv;

	push_call("spell_restoration(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!CAN_CRITICAL(victim))
	{
		act("$N cannot be restored.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act( "{138}Restoring energy fills your body.", victim, NULL, NULL, TO_CHAR);
	act( "{138}Restoring energy fills $n's body.", victim, NULL, NULL, TO_ROOM);
	for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;
		
		switch (paf->location)
		{
			case APPLY_STR_DAMAGE:
			case APPLY_DEX_DAMAGE:
			case APPLY_CON_DAMAGE:
			case APPLY_INT_DAMAGE:
			case APPLY_WIS_DAMAGE:
			case APPLY_CHA_DAMAGE:
				{
					affect_strip(victim,paf->type);
					continue;
				}
				break;

			case APPLY_STR:
			case APPLY_DEX:
			case APPLY_CON:
			case APPLY_INT:
			case APPLY_WIS:
			case APPLY_CHA:
				if (paf->modifier < 0)
				{
					affect_strip(victim,paf->type);
					continue;
				}
				break;
		}

		if (sn == gsn_lesser_restoration)
			continue;						
	
		switch (paf->location)
		{
			case APPLY_STR_DRAIN:
			case APPLY_DEX_DRAIN:
			case APPLY_CON_DRAIN:
			case APPLY_INT_DRAIN:
			case APPLY_WIS_DRAIN:
			case APPLY_CHA_DRAIN:
				{
					affect_strip(victim,paf->type);
					continue;
				}
				break;
			case APPLY_LEVEL:
				if (paf->duration >= 0)
				{
					affect_strip(victim,paf->type);
					continue;
				}
				else if (sn == gsn_greater_restoration)
				{
					affect_strip(victim,paf->type);
					continue;
				}
				else if ((paf->modifier += 1) >= 0)
				{
					affect_strip(victim,paf->type);
					continue;
				}
		}
		
		if (sn == gsn_restoration)
			continue;
			
		if (IS_SET(paf->bitvector, AFF2_CONFUSION))
		{
			affect_strip(victim,paf->type);
			continue;
		}
	}
	if (sn == gsn_lesser_restoration)
		mv = get_max_move(victim) / 3;
	else if (sn == gsn_restoration)
		mv = get_max_move(victim) * 2 / 3;
	else
		mv = get_max_move(victim);
	victim->move = UMIN(victim->move + mv, get_max_move(victim));
	update_pos(victim,-1);

	pop_call();
	return TRUE;
}

DO_SPELL (spell_gentle_repose)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_gentle_repose(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!obj)
	{
		send_to_char("You can't find a corpse here.\n\r", ch);
		pop_call();
		return FALSE;
	}
	
	if (is_obj_affected(obj, gsn_gentle_repose))
	{
		send_to_char("This corpse is already preserved.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!IS_OBJ_TYPE(obj, ITEM_CORPSE_PC) && !IS_OBJ_TYPE(obj, ITEM_CORPSE_NPC))
	{
		send_to_char("That is not a player corpse.\n\r", ch);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	af.type				= sn;
	af.duration		= -1;
	af.bittype		= AFFECT_TO_NONE;
	af.level     	= level;
	af.bitvector	= AFF_NONE;
	af.location		= APPLY_NONE;
	af.modifier		= 0;
	affect_to_obj( ch,obj, &af);

	obj->timer = 1440;	  	

	act("{138}$p has been preserved.", ch, obj, NULL, TO_ALL);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_gust_of_wind)
{
	CHAR_DATA *gch, *gch_next;
	int rtd_type = 0;
	ROOM_TIMER_DATA *rtd, *rtd_next;

	push_call("spell_gust_of_wind(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act("{178}A powerful gust of wind blasts from you!", ch, NULL, NULL, TO_CHAR);
	act("{178}A powerful gust of wind blasts from $n!", ch, NULL, NULL, TO_ROOM);
	
	for (gch = ch->in_room->first_person ; gch ; gch = gch_next)
	{
		gch_next = gch->next_in_room;

		//strip insect plage from victims with the gust
		if (is_affected(gch, gsn_insect_plague))
		{
			act("{038}The insects swarming around you scatter in the winds!", gch, NULL, NULL, TO_CHAR);
			act("{038}The insects swarming around $n scatter in the winds!", gch, NULL, NULL, TO_ROOM);
			affect_strip(gch, gsn_insect_plague);
		}

		if (gch == ch)
			continue;
			
		if (!can_mass_cast(ch, gch, sn))
			continue;

		if (save_resist(ch, gch, sn, level))
			continue;
			
		switch(get_size(gch))
		{
			case SIZE_FINE:
			case SIZE_DIMINUTIVE:
			case SIZE_TINY:
				if (IS_FLYING(gch))
				{
					act("$n is buffeted back by the winds!", gch, NULL, NULL, TO_ROOM);
					act("{118}You are buffeted back by the winds!", gch, NULL, NULL, TO_CHAR);
					gch->distracted = 2;
					damage(ch, gch, dice(2,6), sn, NULL);
				}
				else
				{
					act("$n is bowled over by the winds!", gch, NULL, NULL, TO_ROOM);
					act("{118}You are bowled over by the winds!", gch, NULL, NULL, TO_CHAR);
					gch->distracted = 2;
					update_pos(gch, POS_RESTING);
					damage(ch, gch, dice(1,4), sn, NULL);
				}
				break;
			case SIZE_SMALL:
				if (IS_FLYING(gch))
				{
					if (str_roll(gch) < 20)
					{
						if (!domain_apotheosis(gch, DOMAIN_STRENGTH) || str_roll(gch) < 20)
						{
							act("$n is buffeted back by the winds!", gch, NULL, NULL, TO_ROOM);
							act("{118}You are buffeted back by the winds!", gch, NULL, NULL, TO_CHAR);
							gch->distracted = 2;
							damage(ch, gch, dice(1,6), sn, NULL);
						}
					}
				}
				else
				{
					act("$n is bowled over by the winds!", gch, NULL, NULL, TO_ROOM);
					act("{118}You are bowled over by the winds!", gch, NULL, NULL, TO_CHAR);
					gch->distracted = 2;
					update_pos(gch, POS_RESTING);
				}
				break;
			case SIZE_MEDIUM:
				if (!IS_FLYING(gch))
				{
					if (str_roll(gch) < 15)
					{
						if (!domain_apotheosis(gch, DOMAIN_STRENGTH) || str_roll(gch) < 15)
						{
							act("$n is bowled over by the winds!", gch, NULL, NULL, TO_ROOM);
							act("{118}You are bowled over by the winds!", gch, NULL, NULL, TO_CHAR);
							gch->distracted = 2;
							update_pos(gch, POS_RESTING);
						}
					}
				}
				break;
			default:
				break;
		}
	}
	for (rtd = mud->f_room_timer ; rtd ; rtd = rtd_next)
	{
		rtd_next = rtd->next;
		
		if (rtd->vnum == ch->in_room->vnum && IS_SET(rtd->bitvector, ROOM_FOG))
		{
			rtd->duration = 1;
			rtd_type = rtd->type;
		}
		if (rtd->vnum == ch->in_room->vnum && IS_SET(rtd->bitvector, ROOM_SWARM))
		{
			rtd->duration = 0;
		}
	}
	if (rtd_type != 0)
	{
		act("{168}The clouds are blown back by the winds!", ch, NULL, NULL, TO_ALL);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_remove_curse)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj;
	AFFECT_DATA *paf, *paf_next;
	int paf_type;
	bool removed = FALSE;

	push_call("spell_remove_curse(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for ( paf_type = 0, paf = victim->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;

		if (!IS_SET(paf->bitvector, AFF_CURSE))
			continue;
			
		if (dice(1,20) + level <= 11 + paf->level)
			continue;
			
		if (paf->duration >= 0)
		{
			if (skill_table[paf->type].msg_off)
			{
				act( skill_table[paf->type].msg_off, victim, NULL, NULL, TO_CHAR);
			}
			if (skill_table[paf->type].msg_off_room)
			{
				act( skill_table[paf->type].msg_off_room, victim, NULL, NULL, TO_ROOM);
			}
		}
		paf_type = paf->type;
		affect_from_char(victim, paf);
	}

	for (obj = victim->first_carrying; obj != NULL ; obj = obj->next_content)
	{
		if(IS_SET(obj->extra_flags, ITEM_NOREMOVE))
		{
			REMOVE_BIT( obj->extra_flags, ITEM_NOREMOVE);
			act("$p is no longer cursed.", ch, obj, NULL, TO_ALL);
			removed = TRUE;
		}
	}
	if (paf_type == 0 && !removed)
	{
		send_to_char_color( "Nothing seems to happen.\n\r", ch);	
		send_to_char_color( "Nothing seems to happen.\n\r", victim);	
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_remove_fear)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	AFFECT_DATA af;
	int casterlvl;
	bool scared = FALSE;

	push_call("spell_remove_fear(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	casterlvl = dice(1, 20) + level;

	for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;

		if (IS_SET(skill_table[paf->type].spell_desc, SDESC_FEAR))
		{
			if (casterlvl >= paf->level + 11)
			{
				affect_strip(victim, paf->type);
			}
			else
			{
				scared = TRUE;
			}
		}
	}
	
	if (!scared)
	{
		victim->fear_level = 0;
	
		act( "{138}You become flushed with bravery!", ch, NULL, victim, TO_VICT );
		act( "{138}$N becomes flushed with bravery!", ch, NULL, victim, TO_CHAR );
	}
	else
	{
		act("You fail to quell $N's fears!", ch, NULL, victim, TO_CHAR);
	}

	af.type		= sn;
	af.duration	= hr;
	af.bittype	= AFFECT_TO_NONE;
	af.bitvector	= 0;
	af.location	= APPLY_SAVE_FEAR;
	af.modifier	= UMIN(level/4, 4) + 4;
	af.level     = level;
	affect_join( ch,victim, &af);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_remove_paralysis)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	int paf_type;

	push_call("spell_remove_paralysis(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for ( paf_type = 0, paf = victim->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;

		if (IS_SET(paf->bitvector, AFF2_STAGGERED) || IS_SET(paf->bitvector, AFF2_PARALYSIS))
		{			
			if (dice(1,20) + level <= 11 + paf->level)
				continue;
				
			if (paf->duration >= 0)
			{
				if (skill_table[paf->type].msg_off)
				{
					act( skill_table[paf->type].msg_off, victim, NULL, NULL, TO_CHAR);
				}
				if (skill_table[paf->type].msg_off_room)
				{
					act( skill_table[paf->type].msg_off_room, victim, NULL, NULL, TO_ROOM);
				}
			}
			paf_type = paf->type;
			affect_from_char(victim, paf);
		}
	}

	if (paf_type == 0)
	{
		send_to_char_color( "Nothing seems to happen.\n\r", ch);	
		send_to_char_color( "Nothing seems to happen.\n\r", victim);	
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_freedom)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	int paf_type;

	push_call("spell_freedom(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for ( paf_type = 0, paf = victim->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;

		if (IS_SET(paf->bitvector, AFF2_STAGGERED)
		|| IS_SET(paf->bitvector, AFF2_PARALYSIS)
		|| IS_SET(paf->bitvector, AFF2_PETRIFICATION)
		|| IS_SET(paf->bitvector, AFF2_STAGGERED)
		|| IS_SET(paf->bitvector, AFF_SLEEP)
		|| IS_SET(paf->bitvector, AFF2_ENTANGLED)
		|| IS_SET(paf->bitvector, AFF2_STUNNED))
		{			
			if (paf->duration >= 0)
			{
				if (skill_table[paf->type].msg_off)
				{
					act( skill_table[paf->type].msg_off, victim, NULL, NULL, TO_CHAR);
				}
				if (skill_table[paf->type].msg_off_room)
				{
					act( skill_table[paf->type].msg_off_room, victim, NULL, NULL, TO_ROOM);
				}
			}
			paf_type = paf->type;
			affect_from_char(victim, paf);
		}
	}

	if (paf_type == 0)
	{
		send_to_char_color( "Nothing seems to happen.\n\r", ch);	
		send_to_char_color( "Nothing seems to happen.\n\r", victim);	
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_revive)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int move, cond;

	push_call("spell_revive(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	move = UMIN(level * 10, 200);
	
	victim->move = UMIN( victim->move + move, victim->max_move );

	act( "{138}$n beams with renewed energy.", victim, NULL, NULL, TO_ROOM );
	act( "{138}You feel revitalized!", victim, NULL, NULL, TO_CHAR );
	
	if (!IS_NPC(victim))
	{
		cond = 48 - victim->pcdata->condition[COND_FULL];
		gain_condition(victim, COND_FULL, cond);

		cond = 48 - victim->pcdata->condition[COND_THIRST];
		gain_condition(victim, COND_THIRST, cond);
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_rusting_grasp)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj;
	int cnt, pick;
	bool hit = FALSE;

	push_call("spell_rusting_grasp(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	switch (race_table[get_race(victim)].material)
	{
		case MATERIAL_STEEL:
		case MATERIAL_COLD_IRON:
			damage( ch, victim, spell_dice(ch, sn, 3, 6) + UMIN(level, 15), sn, NULL );
			pop_call();
			return TRUE;
		default:
			break;
	}
	
	for (cnt = 0, obj = victim->first_carrying ; obj ; obj = obj->next)
	{
		if (!IS_WORN(obj))
		{
			continue;
		}
		if (obj->material != MATERIAL_STEEL
		&& obj->material != MATERIAL_COLD_IRON)
		{
			continue;
		}
		if (!IS_OBJ_TYPE(obj, ITEM_ARMOR) || IS_OBJ_STAT(obj, ITEM_MAGIC|ITEM_BROKEN))
		{
			continue;
		}
		cnt++;
	}
	if (cnt)
	{
		pick = number_range(1,cnt);
	
		for (obj = victim->first_carrying ; obj ; obj = obj->next)
		{
			if (!IS_WORN(obj))
			{
				continue;
			}
			if (obj->material != MATERIAL_STEEL
			&& obj->material != MATERIAL_COLD_IRON)
			{
				continue;
			}
			if (!IS_OBJ_TYPE(obj, ITEM_ARMOR) || IS_OBJ_STAT(obj, ITEM_MAGIC|ITEM_BROKEN))
			{
				continue;
			}
			if (cnt == pick)
			{
				damage_equipment(ch, victim, obj, dice(1,6) + UMIN(level/2,10), sn, NULL);
				hit = TRUE;
				break;
			}
		}
	}
	if (!hit)
	{
		act("Your touch had no affect.", ch, NULL, NULL, TO_CHAR);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_sanctify)
{
	ROOM_INDEX_DATA *room;
	ROOM_TIMER_DATA rtd;
	CHAR_DATA *fch;
	char buf[MAX_INPUT_LENGTH];

	push_call("spell_sanctify(%p,%p,%p,%p)",sn,level,ch,vo);

	if (ch->in_room == NULL)
	{
		pop_call();
		return FALSE;
	}

	room = ch->in_room;

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

	for (fch = room->first_person ; fch ; fch = fch->next_in_room)
	{
		if (in_combat(fch))
		{
			send_to_char( "There is too much violence present to sanctify.\n\r", ch );
			pop_call();
			return FALSE;
		}
	}

	if (ch->in_room->area->low_r_vnum == ROOM_VNUM_ARENA)
	{
		send_to_char("The arena may not be sanctified.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= hr * level;
	rtd.bitvector	= ROOM_SAFE;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	send_to_char( "The area becomes sanctified, and is a sanctuary for all.\n\r", ch);
	sprintf(buf, "$n prays to %s and makes this area a sanctuary to all.", get_god_name(which_god(ch)));
	act( buf, ch, NULL, NULL, TO_ROOM);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_scintillating_pattern)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch;
	AFFECT_DATA af;
	int count, lvl, duration;

	push_call("spell_scintillating_pattern(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act("{138}You weave {118}a tw{128}isti{138}ng pa{148}ttern{158} of co{168}rusca{118}ting c{128}olors.", ch, NULL, NULL, TO_CHAR);
	act("{138}$n weaves {118}a tw{128}isti{138}ng pa{148}ttern{158} of co{168}rusca{118}ting c{128}olors.", ch, NULL, NULL, TO_ROOM);

	for (count = lvl = 0 ; lvl <= level ; lvl++)
	{
		for (gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
		{
			if (gch->level != lvl)
				continue;

			if (!is_same_group(victim, gch))
				continue;
				
			if (count + gch->level > level)
			{
				act( "$N resists your spell.\n\r", ch, NULL, gch, TO_CHAR );
				continue;
			}
			
			if (save_resist(ch, gch, sn, level))
				continue;
	
			duration = dice(1,4);
			
			act("{138}OOooooh... pretty colors....", ch, NULL, gch, TO_VICT);
			
			af.type      = sn;
			af.duration  = gch->level <= 6 ? duration * 3 : gch->level <= 12 ? duration * 2 : duration;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_CONFUSION;
			af.level     = level;
			affect_to_char( ch, gch, &af );

			if (gch->level <= 12)
			{
				af.duration  = gch->level <= 6 ? duration * 2 : duration;
				af.location  = APPLY_NONE;
				af.modifier  = 0;
				af.bittype   = AFFECT_TO_CHAR;
				af.bitvector = AFF2_STUNNED;
				af.level     = level;
				affect_to_char( ch, gch, &af );
			}	

			if (gch->level <= 6)
			{
				af.duration  = duration;
				af.location  = APPLY_NONE;
				af.modifier  = 0;
				af.bittype   = AFFECT_TO_CHAR;
				af.bitvector = AFF2_UNCONSCIOUS;
				af.level     = level;
				affect_to_char( ch, gch, &af );
			}

			count += gch->level;
			if (count >= level)
				break;
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_scrying)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	OBJ_DATA *obj;
	AFFECT_DATA af;

	push_call("spell_scrying(%p,%p,%p,%p)",sn,level,ch,vo);

	if (victim == ch)
	{
		send_to_char("You see yourself perfectly clearly.", ch);
		pop_call();
		return FALSE;
	}

	if ((obj = get_obj_wear_type(ch, ITEM_WINDOW)) == NULL)
	{
		send_to_char("You are not holding a mirror to scry with.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if ((victim = get_player_world(ch, target_name)) == NULL || !victim->in_room)
	{
		send_to_char("You cannot locate anyone by that name.\n\r", ch);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (IS_SET(victim->in_room->room_flags, ROOM_NOSCRY)
	||  victim->in_room->sector_type == SECT_ASTRAL
	||  victim->in_room->sector_type == SECT_ETHEREAL)
	{
		send_to_char("You cannot envision your subject.\n\r", ch);
		pop_call();
		return TRUE;
	}
	
	if (!knows_char(ch, victim))
	{
		send_to_char("You are not familiar enough with your subject.\n\r", ch);
		pop_call();
		return TRUE;
	}
	
	if (check_nondetection(ch, victim, gsn_scrying))
	{
		send_to_char("Your subject is wared against your scrying.\n\r", ch);
		pop_call();
		return TRUE;
	}
	
	if (is_affected(victim, gsn_detect_scrying))
	{
		if (dice(1,20) + get_affect_level(victim, gsn_detect_scrying) >= dice(1,20) + level)
			act("You sense that $n is watching you.", ch, NULL, victim, TO_VICT);
		else
			act("You sense that someone is watching you.", ch, NULL, victim, TO_VICT);
	}	

	if (save_resist(ch, victim, sn, level))
	{
		act("$N resists your attempt to scry $M.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return TRUE;
	}
	
	af.type		= sn;
	af.duration	= turn * level;
	af.bittype	= AFFECT_TO_OBJ;
	af.level     = level;
	af.bitvector	= ITEM_MAGIC;
	af.location	= APPLY_OBJVAL_0;
	af.modifier	= victim->in_room->vnum;
	affect_to_obj( ch, obj, &af);
	obj->owned_by = ch->pcdata->pvnum;

	act( "$p {178}attunes your vision to $N.", ch, obj, victim, TO_CHAR);
	act( "$p {178}glows momentarily in $n's hand.", ch, obj, NULL, TO_ROOM);
	
	look_scrying(ch, obj, "");

	pop_call();
	return TRUE;
}

DO_SPELL(spell_searing_light)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	int diceroll, save;

	push_call("spell_searing_light(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act("{178}A bright beam of light bursts from your hand!", victim, NULL, NULL, TO_CHAR);
	act("{178}A bright beam of light bursts from $n's hand!", victim, NULL, NULL, TO_ROOM);

	if ((save = save_resist(ch, victim, sn, level)) == TRUE)
	{
		pop_call();
		return TRUE;
	}

	if (!save)
	{
		af.type      = sn;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.level     = level;
		af.duration  = URANGE(1, level/2, 5);
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_DAZZLED;
		affect_join( ch, victim, &af );
		act("{178}You are dazzled by the brilliant light!", victim, NULL, NULL, TO_CHAR);
	}
	
	if (IS_UNDEAD(victim))
	{
		if (race_skill(victim, gsn_light_vulnerability))
			diceroll = spell_dice(ch, sn, UMIN(level, 10), 8);
		else
			diceroll = spell_dice(ch, sn, UMIN(level, 10), 6);
	}
	else if (race_type(victim) == RTYPE_CONSTRUCT)
	{
		diceroll = spell_dice(ch, sn, URANGE(1, level/2, 5), 6);
	}
	else
	{
		diceroll = spell_dice(ch, sn, URANGE(1, level/2, 5), 8);
	}
	
	damage(ch, victim, diceroll, sn, NULL);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_sending)
{
	CHAR_DATA *victim;
	char arg[MAX_INPUT_LENGTH];
	
	push_call("spell_sending(%p,%p,%p,%p)",sn,level,ch,vo);

	target_name = one_argument(target_name, arg);

	if (arg[0] == '\0' || target_name[0] == '\0')
	{
		send_to_char ("Tell whom what?\n\r", ch);
		pop_call();
		return FALSE;
	}

	if ((victim = get_player_world(ch, arg)) == NULL)
	{
		send_to_char ("They aren't here.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (victim->desc == NULL)
	{
		ch_printf_color(ch, "That player is link-dead.\n\r");
		pop_call();
		return FALSE;
	}

	if (victim->desc && victim->desc->connected == CON_EDITING && get_trust (ch) < LEVEL_IMMORTAL)
	{
		ch_printf_color(ch, "They are currently in a writing buffer.  Please try again in a few minutes.\n\r");
		pop_call();
		return FALSE;
	}
	
	if (blocking(victim, ch) || IS_MUTED(victim))
	{
		ch_printf_color(ch, "That person refuses to hear you.\n\r");
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (!IS_IMMORTAL(ch) && !knows_char(ch, victim))
	{
		ch_printf_color(ch, "That person refuses to hear you.\n\r");
		pop_call();
		return TRUE;
	}

	if (IS_SET(victim->act, PLR_AFK))
	{
		ch_printf_color(ch, "That player is afk and may not see your message.\n\r");
	}
	
	tell(ch, victim, target_name);

	if (!IS_NPC(ch) && !IS_NPC(victim))
	{
		victim->pcdata->reply = ch;
	}

	pop_call();
	return TRUE;
}


DO_SPELL(spell_shillelagh)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_shillelagh(%p,%p,%p,%p)",sn,level,ch,vo);

	if (obj->item_type != ITEM_WEAPON)
	{
		send_to_char( "That object cannot be enchanted.\n\r", ch);
		pop_call();
		return FALSE;
	}
	
	if (IS_OBJ_STAT(obj, ITEM_MAGIC))
	{
		act( "$p is already enchanted.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	switch (obj->value[0])
	{
		case WEAPON_TYPE_QUARTERSTAFF:
		case WEAPON_TYPE_CLUB:
		case WEAPON_TYPE_GREATCLUB:
			break;
		default:
			send_to_char( "You can only enchant a club or staff.\n\r", ch);
			pop_call();
			return FALSE;
	}	

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	af.type				= sn;
	af.duration		= turn * level;
	af.bittype		= AFFECT_TO_OBJ;
	af.level    	= level;
	af.bitvector	= ITEM_MAGIC;

	af.location	= APPLY_HITROLL;
	af.modifier	= 1;
	affect_to_obj( ch,obj, &af);

	af.location	= APPLY_DAMROLL;
	af.modifier	= 1;
	affect_to_obj( ch,obj, &af);

	act( "$p {138}glows {078}momentarily in your hand.", ch, obj, NULL, TO_CHAR);
	act( "$p {138}glows {078}momentarily in $n's hand.", ch, obj, NULL, TO_ROOM);
	pop_call();
	return TRUE;
}

DO_SPELL(spell_shocking_grasp)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam;

	push_call("spell_shocking_grasp(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	dam = spell_dice(ch, sn, UMIN(level, 5), 6);

	spell_damage(ch, victim, dam, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_shout)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int diceroll;

	push_call("spell_shout(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	diceroll = spell_dice(ch, sn, URANGE(5, level/2, 10), 6);

	spell_damage(ch, victim, diceroll, sn, level);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_sleep)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch;
	AFFECT_DATA af;
	int count, lvl, dice;

	push_call("spell_sleep(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (sn == gsn_sleep)
		dice = 4;
	else if (sn == gsn_deep_slumber)
		dice = 10;
	else
		dice = level;

	for (count = lvl = 0 ; lvl <= dice ; lvl++)
	{
		for (gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
		{
			if (gch->level != lvl)
				continue;

			if (!is_same_group(victim, gch))
				continue;
				
			if (IS_AFFECTED(gch, AFF_SLEEP) )
				continue;
			
			if (count + gch->level > dice)
			{
				act( "$N resists your sleep spell.\n\r", ch, NULL, gch, TO_CHAR );
				continue;
			}
			if (sn == gsn_power_word_sleep && gch->hit > 100)
			{
				act( "$N resists your sleep spell.\n\r", ch, NULL, gch, TO_CHAR );
				continue;
			}
			
			if (save_resist(ch, gch, sn, level))
				continue;
	
			af.type      = sn;
			af.duration  = turn * level;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_SLEEP;
			af.level     = level;
			affect_to_char( ch, gch, &af );
	
			if (IS_AWAKE(gch))
			{
				/* Add to make fighting people stop fighting to sleep */
				stop_fighting(gch, FALSE);
				act( "You feel very sleepy ..... zzzzzz.", gch, NULL, NULL, TO_CHAR );
				act( "$n closes $s eyes and drifts to sleep.", gch, NULL, NULL, TO_ROOM );
				update_pos(gch, POS_RESTING);
				count += gch->level;
			}
			if (count >= dice)
				break;
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_sleet_storm)
{
	ROOM_TIMER_DATA rtd;

	push_call("spell_sleet_storm(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(ch->in_room->room_flags, ROOM_ICE))
	{
		send_to_char("This area is already covered by ice.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (IS_SET(ch->in_room->room_flags, ROOM_SAFE))
	{
		send_to_char( "Wards prevent you from doing that here.\n\r", ch );
		pop_call();
		return FALSE;
	}

	switch (ch->in_room->sector_type)
	{
		case SECT_ETHEREAL:
		case SECT_ASTRAL:
		case SECT_UNDER_WATER:
			send_to_char("That cannot be cast here.\n\r", ch);
			pop_call();
			return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= ch->in_room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= level;
	rtd.bitvector	= ROOM_ICE;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{178}Sleet begins to fall at your command, freezing into sheets of ice.", ch, NULL, NULL, TO_CHAR);
	act( "{178}Sleet falls at $n's command, freezing into sheets of ice.", ch, NULL, NULL, TO_ROOM);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_snake_dart)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	int dam, save;

	push_call("spell_snake_dart(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	dam = spell_dice(ch, sn, 1, 6);

	if (can_backstab(ch, victim, sn) && number_percent() > get_apply(victim, APPLY_FORTIFICATION))
	{
		int bs_dice = ROUNDUP(multi_skill_level(ch, gsn_backstab) / 2);
		int bs_dam = 0;

		if (get_monk_style(ch) == STYLE_SLEEPING_TIGER && class_level(ch, CLASS_MONK) >= 12)
			bs_dice++;

		if (bs_dice > 0)
		{
			if (learned(ch, gsn_greater_sneak_attack))
			{
				bs_dam = dice(bs_dice, 8);
			}
			else
			{
				bs_dam = dice(bs_dice, 6);
			}
		}
		dam += bs_dam;
	}

	if ((save = save_resist(ch, victim, sn, level)) == TRUE)
	{
		damage(ch, victim, dam, sn, NULL);
	}
	else
	{
		damage(ch, victim, dam, sn, NULL);

		if (save != PARTIAL)
		{
			if (!IS_AFFECTED(victim, AFF_POISON))
			{
				AFFECT_DATA af;
	
				af.type      = gsn_poison;
				af.location  = APPLY_CON;
				af.modifier  = 0-spell_dice(ch, sn, 1,10);
				af.duration  = level;
				af.bittype   = AFFECT_TO_CHAR;
				af.bitvector = AFF_POISON;
				af.level     = level;
	
				affect_to_char( ch, victim, &af );
	
				send_to_char( "You feel poison coursing through your veins.\n\r", victim );
				if (ch != victim)
				{
					act( "$N now feels your poison.\n\r", ch, NULL, victim, TO_CHAR );
				}
			}
		}
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_private_sanctum)
{
	ROOM_INDEX_DATA *room;
	ROOM_TIMER_DATA rtd;

	push_call("spell_private_sanctum(%p,%p,%p,%p)",sn,level,ch,vo);

	if (ch->in_room == NULL)
	{
		pop_call();
		return FALSE;
	}

	room = ch->in_room;

	if (IS_SET(room->room_flags, ROOM_PRIVATE|ROOM_NOSCRY))
	{
		send_to_char( "This place is already warded against such intrusion.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= 2 * hr * level;
	rtd.bitvector	= ROOM_PRIVATE|ROOM_NOSCRY;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{108}The area becomes secure from external observation.", ch, NULL, NULL, TO_ALL);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_dimensional_lock)
{
	ROOM_INDEX_DATA *room;
	ROOM_TIMER_DATA rtd;

	push_call("spell_dimensional_lock(%p,%p,%p,%p)",sn,level,ch,vo);

	if (ch->in_room == NULL)
	{
		pop_call();
		return FALSE;
	}

	room = ch->in_room;

	if (IS_SET(room->room_flags, ROOM_NO_ASTRAL))
	{
		send_to_char( "This place is already warded against dimensional travel\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= hr * level;
	rtd.bitvector	= ROOM_NO_ASTRAL;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	act( "{128}The area glows with a shimmering emerald barrier.", ch, NULL, NULL, TO_ALL);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_rainbow_pattern)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch;
	AFFECT_DATA af;
	int count, lvl;

	push_call("spell_rainbow_pattern(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act("{138}A glowing {118}rain{128}bow{138} patt{148}ern sw{158}irls {168}befor{118}e you.", ch, NULL, NULL, TO_ALL);

	for (count = lvl = 0 ; lvl <= 24 ; lvl++)
	{
		for (gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
		{
			if (gch->level != lvl)
				continue;

			if (!is_same_group(victim, gch))
				continue;
				
			if (count + gch->level > level)
			{
				act( "$N resists your spell.\n\r", ch, NULL, gch, TO_CHAR );
				continue;
			}
			
			if (save_resist(ch, gch, sn, level))
				continue;
	
			act("{138}OOooooh... pretty colors....", ch, NULL, gch, TO_VICT);
			
			af.type      = sn;
			af.duration  = level;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_FASCINATED;
			af.level     = level;
			affect_to_char( ch, gch, &af );

			count += gch->level;
			if (count >= 24)
				break;
		}
	}
	pop_call();
	return TRUE;
}

/*
 * Retribution domain spell c/o Paizo - Kregor
 */
DO_SPELL(spell_rebuke)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int vchsave, dam;

	push_call("spell_rebuke(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!CAN_TALK(ch))
	{
		act("You are unable to utter your rebuke.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "{138}$n rebukes his foes!", ch, NULL, NULL, TO_ROOM );
	act( "{138}You rebuke your foes!", ch, NULL, NULL, TO_CHAR );

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;
		
		if (!can_mass_cast(ch, vch, sn))
			continue;

		if (!is_same_group(victim, vch) && who_fighting(vch) != ch)
			continue;

		if ((vchsave = save_resist(ch, vch, sn, level)) == TRUE)
			continue;
			
		if (which_god(ch) == which_god(vch))
			dam = spell_dice(ch, sn, UMIN(level, 10), 6);
		else
			dam = spell_dice(ch, sn, UMIN(level/2, 8), 8);
			
		if (vchsave == PARTIAL)
			dam /= 2;
		
		dam /= 2;
		damage(ch, vch, dam, sn, NULL);
		damage(ch, vch, dam, gsn_divine_hit, NULL);

		if (!valid_fight(ch, vch))
			continue;

		if (!vchsave)
		{
			if (which_god(ch) == which_god(vch))
			{
				af.type      = sn;
				af.duration  = dice(1,4);
				af.location  = 0;
				af.modifier  = 0;
				af.bittype   = AFFECT_TO_CHAR;
				af.bitvector = AFF2_STUNNED;
				af.level     = level;
				affect_join( ch, vch, &af );
			}
			else
			{
				af.type      = sn;
				af.duration  = 1;
				af.location  = 0;
				af.modifier  = 0;
				af.bittype   = AFFECT_TO_CHAR;
				af.bitvector = AFF2_STAGGERED;
				af.level     = level;
				affect_join( ch, vch, &af );
			}
		}
	}
	pop_call();
	return TRUE;
}


/*
 * Target a player corpse to be able to
* send tells back and forth to player - Kregor
 */
DO_SPELL (spell_speak_with_dead)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	CHAR_DATA *victim;
	PLAYER_GAME *fpl;
	AFFECT_DATA af;
	char buf[MAX_STRING_LENGTH];

	push_call("spell_speak_with_dead(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_NPC(ch))
	{
		pop_call();
		return FALSE;
	}
	
	if (!obj)
	{
		send_to_char("You can't find a corpse here.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!IS_OBJ_TYPE(obj, ITEM_CORPSE_PC) && !IS_OBJ_TYPE(obj, ITEM_CORPSE_NPC))
	{
		send_to_char("That is not a corpse.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	// if corpse is npc, summon GM to play the spirit - Kregor
	if (IS_OBJ_TYPE(obj, ITEM_CORPSE_NPC))
	{
		act( "{108}You attempt to reach the spirit of $p.", ch, obj, NULL, TO_CHAR);
		act( "{108}OOC: Attempting to find a GM to interact with you...", ch, obj, NULL, TO_CHAR);

		for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
		{
			if (!IS_IMMORTAL(fpl->ch))
				continue;
			if (fpl->ch == ch)
				continue;
	
			sprintf(buf, "%s[IMM] $n is attempting SPEAK WITH DEAD upon $p.", get_color_string(fpl->ch, COLOR_TALK, VT102_BOLD));
			act(buf, ch, obj, fpl->ch, TO_VICT);
		}
		pop_call();
		return TRUE;
	}

	if ((victim = get_char_pvnum(obj->owned_by)) == NULL)
	{
		send_to_char("The spirit cannot be found.\n\r", ch);
		pop_call();
		return FALSE;
	}
	if (!IS_PLR(victim, PLR_DEAD))
	{
		send_to_char("The spirit has found a new vessel already!\n\r", ch);
		pop_call();
		return FALSE;
	}
	if (IS_PLR(victim, PLR_SOULBIND))
	{
		send_to_char("The spirit has been bound from the mortal plane.\n\r", ch);
		pop_call();
		return TRUE;
	}

	af.type      = sn;
	af.location  = APPLY_NONE;
	af.duration  = level * turn;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = AFF_NONE;
	af.level     = level;
	affect_join( ch, victim, &af);
	
	act( "{108}You see a spiritual manifestation of $N.", ch, NULL, victim, TO_CHAR);
	act( "{108}You see a spiritual manifestation of $n.", ch, NULL, victim, TO_VICT);
	
	pop_call();
	return TRUE;
}


DO_SPELL(spell_suggestion)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_suggestion(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_AFFECTED(victim, AFF_DOMINATE))
	{
		act("$N is already under someone's influence.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (!can_understand(victim, ch, FALSE))
	{
		act("$N cannot understand your suggestion.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}
	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}
	af.type      = sn;
	af.location  = APPLY_NONE;
	af.duration  = hr * level;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_join( ch,victim, &af);

	act( "{138}$n's words seem so compelling.",  ch, NULL, victim, TO_VICT);
	act( "$N {138}seems to hang onto your words.", ch, NULL, victim, TO_CHAR );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_suffocate)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	ROOM_INDEX_DATA *room;
	ROOM_TIMER_DATA rtd;

	push_call("spell_suffocate(%p,%p,%p,%p)",sn,level,ch,vo);

	if ((room = victim->in_room) == NULL)
	{
		pop_call();
		return FALSE;
	}

	if (IS_SET(room->room_flags, ROOM_SAFE))
	{
		send_to_char( "You cannot remove the air from this area.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (IS_SET(room->room_flags, ROOM_NO_AIR))
	{
		send_to_char( "This place is already devoid of oxygen.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	rtd.vnum			= room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= 0;
	rtd.duration	= level;
	rtd.bitvector	= ROOM_NO_AIR;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	if (ch->in_room != room)
		act( "{108}The area around $N suddenly becomes devoid of breathable air.", ch, NULL, victim, TO_CHAR);
	act( "{108}The area around you suddenly becomes devoid of breathable air.", victim, NULL, NULL, TO_CHAR);
	act( "{108}The area around you suddenly becomes devoid of breathable air.", victim, NULL, NULL, TO_ROOM);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_sunbeam)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	ROOM_INDEX_DATA *tar_room; //might need in case victim dies before spell runs out
	int diceroll, count, save;

	push_call("spell_sunbeam(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if ((tar_room = victim->in_room) == NULL)
	{
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act("{178}bright beams of {138}sunlight {178}burst from your hand!", ch, NULL, NULL, TO_CHAR);
	act("{178}bright beams of {138}sunlight {178}burst from $n's hand!", ch, NULL, NULL, TO_ROOM);

	//1 sunbeam for every 3 caster levels
	for (count = UMIN(level/3, 6), vch = tar_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;
		
		if (vch == victim || is_same_group(vch, victim))
		{
			if ((save = save_resist(ch, vch, sn, level)) != TRUE)
			{
				if (!save)
				{
					af.type      = sn;
					af.location  = APPLY_NONE;
					af.modifier  = 0;
					af.level     = level;
		
					af.duration  = -1;
					af.bittype   = AFFECT_TO_CHAR;
					af.bitvector = AFF_BLIND;
					affect_join( ch, vch, &af );
					act("The light burns your eyes and you cannot see!", vch, NULL, NULL, TO_CHAR);
					
					affect_strip_desc(vch, SDESC_DARKNESS);
				}
				
				if (IS_UNDEAD(vch) || race_type(vch) == RTYPE_OOZE)
					diceroll = spell_dice(ch, sn, UMIN(level, 20), 6);
				else
					diceroll = spell_dice(ch, sn, 4, 6);
				if (race_skill(vch, gsn_light_vulnerability))
					diceroll *= 2;
					
				if (save == PARTIAL)
					diceroll /= 2;

				damage(ch, vch, diceroll, sn, NULL);
			}
			//beam is used up, even if the beam is resisted
			--count;

			if (count <= 0)
				break;
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_sunburst)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	ROOM_INDEX_DATA *tar_room; //might need in case victim dies before spell runs out
	int diceroll, save;

	push_call("spell_sunburst(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if ((tar_room = victim->in_room) == NULL)
	{
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	act("{178}A large solar flare bursts around you!", victim, NULL, NULL, TO_ALL);
	if (ch->in_room != victim->in_room)
		act("{178}A large solar flare bursts around $N!", ch, NULL, victim, TO_ALL);

	affect_room_strip_desc(tar_room, SDESC_DARKNESS);

	for (vch = tar_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;
		
		if (!can_mass_cast(ch, vch, sn))
			continue;

		if ((save = save_resist(ch, vch, sn, level)) == TRUE)
			continue;

		if (!save)
		{
			af.type      = sn;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.level     = level;

			af.duration  = -1;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF_BLIND;
			affect_join( ch, vch, &af );
			act("The light burns your eyes and you cannot see!", vch, NULL, NULL, TO_CHAR);
			
			affect_strip_desc(vch, SDESC_DARKNESS);
		}
		
		if (IS_UNDEAD(vch) || race_type(vch) == RTYPE_OOZE)
			diceroll = spell_dice(ch, sn, UMIN(level, 25), 6);
		else
			diceroll = spell_dice(ch, sn, 6, 6);
		if (race_skill(vch, gsn_light_vulnerability))
			diceroll *= 2;
			
		if (save == PARTIAL)
			diceroll /= 2;

		damage(ch, vch, diceroll, sn, NULL);
	}
	pop_call();
	return TRUE;
}


/*
 * Pipe all summon X spells though these functions - Kregor
 */
DO_SPELL(spell_summon_creature)
{
	MOB_INDEX_DATA *pMob;
	CHAR_DATA *mh;
	int race, hitdice, duration, count, bloodline, cnt, rdm;
	bool fMatch = FALSE;

	push_call("spell_summon_creature(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!is_string(skill_table[sn].name))
	{
		bug("spell_summon_creature: no skill name for sn %d", sn);
		act("Something went wrong.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	hitdice = skill_table[sn].native_level * 4 / 3;

	if (sn == gsn_summon_swarm)
	{
		race = RACE_SWARM_RATS;
		duration = UMAX(2, level / 2);
		count = 1;
	}
	else if (sn == gsn_shambler)
	{
		race = RACE_SHAMBLING_MOUND;
		duration = hr * 72;
		count = dice(1,4) + 2;
	}
	else
	{
		// choose a race based on hitdice. nature's ally depends on sector
		// summon monster picks random extraplanar creature - Kregor
		for (cnt = race = 0 ; race < MAX_RACE ; race++)
		{
			if (race_table[race].hit_dice != hitdice && race_table[race].hit_dice != hitdice + 1)
				continue;

			if (sn == gsn_natures_ally_I || sn == gsn_natures_ally_II || sn == gsn_natures_ally_III
			|| sn == gsn_natures_ally_IV || sn == gsn_natures_ally_V || sn == gsn_natures_ally_VI
			|| sn == gsn_natures_ally_VII || sn == gsn_natures_ally_VIII || sn == gsn_natures_ally_IX)
			{
	// 			if (!IS_SET(race_table[race].sectors, 1 << ch->in_room->sector_type))
	// 				continue;
				if (race_table[race].type != RTYPE_ANIMAL 
				&& race_table[race].type != RTYPE_MAGICAL
				&& race_table[race].type != RTYPE_VERMIN
				&& race_table[race].type != RTYPE_FEY)
					continue;
				fMatch = TRUE;
				cnt++;
			}
			else if (sn == gsn_summon_monster_I || sn == gsn_summon_monster_II || sn == gsn_summon_monster_III
			|| sn == gsn_summon_monster_IV || sn == gsn_summon_monster_V || sn == gsn_summon_monster_VI
			|| sn == gsn_summon_monster_VII || sn == gsn_summon_monster_VIII || sn == gsn_summon_monster_IX)
			{
				if (!IS_SET(race_table[race].flags, RSPEC_EXTRAPLANAR))
					continue;
				fMatch = TRUE;
				cnt++;
			}
			else if (sn == gsn_create_lesser_undead
			|| sn == gsn_create_minor_undead
			|| sn == gsn_create_greater_undead
			|| sn == gsn_create_undead)
			{
				if (race_table[race].type != RTYPE_UNDEAD)
					continue;
				if (race_table[race].nonability[STAT_INT])
					continue;
				fMatch = TRUE;
				cnt++;
			}
		}
		if (!fMatch)
		{
			act( "You can't seem to summon forth an ally.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}

		rdm = number_range(1,cnt);

		for (cnt = race = 0 ; race < MAX_RACE ; race++)
		{
			if (race_table[race].hit_dice != hitdice && race_table[race].hit_dice != hitdice + 1)
				continue;

			if (sn == gsn_natures_ally_I || sn == gsn_natures_ally_II || sn == gsn_natures_ally_III
			|| sn == gsn_natures_ally_IV || sn == gsn_natures_ally_V || sn == gsn_natures_ally_VI
			|| sn == gsn_natures_ally_VII || sn == gsn_natures_ally_VIII || sn == gsn_natures_ally_IX)
			{
	// 			if (!IS_SET(race_table[race].sectors, 1 << ch->in_room->sector_type))
	// 				continue;
				if (race_table[race].type != RTYPE_ANIMAL 
				&& race_table[race].type != RTYPE_MAGICAL
				&& race_table[race].type != RTYPE_VERMIN
				&& race_table[race].type != RTYPE_FEY)
					continue;
				if (cnt++ == rdm)
					break;
			}
			else if (sn == gsn_summon_monster_I || sn == gsn_summon_monster_II || sn == gsn_summon_monster_III
			|| sn == gsn_summon_monster_IV || sn == gsn_summon_monster_V || sn == gsn_summon_monster_VI
			|| sn == gsn_summon_monster_VII || sn == gsn_summon_monster_VIII || sn == gsn_summon_monster_IX)
			{
				if (!IS_SET(race_table[race].flags, RSPEC_EXTRAPLANAR))
					continue;
				if (cnt++ == rdm)
					break;
			}
			else if (sn == gsn_create_lesser_undead
			|| sn == gsn_create_minor_undead
			|| sn == gsn_create_greater_undead
			|| sn == gsn_create_undead)
			{
				if (race_table[race].type != RTYPE_UNDEAD)
					continue;
				if (race_table[race].nonability[STAT_INT])
					continue;
				if (cnt++ == rdm)
					break;
			}
		}
		duration = level;
		count = 1;
	}

	pMob = get_mob_index(MOB_VNUM_SUMMONS);

	while (count)
	{
		mh = create_mobile(pMob);
		char_to_room( mh, ch->in_room->vnum, TRUE );
	
		mh->race				= race;
		mh->level				= race_table[race].hit_dice;
		mh->perm_str		= 10 + race_table[race].race_mod[0];
		mh->perm_dex		= 10 + race_table[race].race_mod[1];
		mh->perm_con		= 10 + race_table[race].race_mod[2];
		mh->perm_int		= 10 + race_table[race].race_mod[3];
		mh->perm_wis		= 10 + race_table[race].race_mod[4];
		mh->perm_cha		= 10 + race_table[race].race_mod[5];
		mh->size				= race_table[race].size;
		mh->height			= race_table[race].height;
		mh->weight			= race_table[race].weight;
		mh->speak				= race_table[race].speaks;
		mh->language		= race_table[race].understands;
		mh->alignment		= race_table[race].alignment;
		mh->ethos				= race_table[race].ethos;
		
		if (sn == gsn_create_lesser_undead
		|| sn == gsn_create_minor_undead
		|| sn == gsn_create_greater_undead
		|| sn == gsn_create_undead)
		{
			if (arcane_mastery(ch, SCHOOL_NECROMANCY))
			{
				mh->perm_str += 4;
				mh->max_hit  += mh->level * 2;
				
				AFFECT_DATA af;
				af.type      = sn;
				af.duration  = -1;
				af.location  = APPLY_TURN_RESIST;
				af.modifier  = 4;
				af.bittype   = AFFECT_TO_NONE;
				af.bitvector = 0;
				af.level     = level;
				affect_to_char( ch, mh, &af );
			}
		}
		else
		{
			if (learned(ch, gsn_augment_summoning))
			{
				mh->perm_str += 4;
				mh->perm_con += 4;
			}		
			if (arcane_mastery(ch, SCHOOL_CONJURATION))
			{
				mh->perm_str += 4;
				mh->perm_con += 4;
			}
		}
				
		mh->max_hit		= dice(mh->level, race_type_table[race_table[race].type].hit_die);
		mh->max_hit		= UMAX(mh->level * race_type_table[race_table[race].type].hit_die / 2, mh->max_hit);
		mh->hit				= get_max_hit(mh);
		
		RESTRING(mh->name, format("summoned %s", race_table[race].race_name));
		RESTRING(mh->short_descr, format("a summoned %s", race_table[race].race_name));
		RESTRING(mh->long_descr, format("A summoned %s is here.", race_table[race].race_name));
		mh->npcdata->sac_timer = duration;
		
		if (ch->cast_class == CLASS_SORCERER)
		{
			if ((bloodline = get_bloodline(ch)) == BLOODLINE_CELESTIAL || bloodline == BLOODLINE_ABYSSAL)
			{
				AFFECT_DATA af;
				
				af.type      = sn;
				if (bloodline == BLOODLINE_ABYSSAL)
					af.location  = APPLY_DR_GOOD;
				else
					af.location  = APPLY_DR_EVIL;
				af.modifier  = UMAX(1, class_level(ch, CLASS_SORCERER) / 2);
				af.level     = level;
				af.duration  = -1;
				af.bittype   = AFFECT_TO_NONE;
				af.bitvector = AFF_NONE;
				affect_join( ch, mh, &af );
			}
		}
			
		if (!IS_UNDEAD(mh))
		{
			mh->npcdata->sac_string = STRALLOC("{178}$n shimmers and vanishes!");
			act( "{178}$n appears in a shimmer of light.", mh, NULL, NULL, TO_ALL);
		}
		else
		{
			mh->npcdata->sac_string = STRALLOC("{108}$n disperses into inky blackness");
			act( "{108}$n appears in a coalescing black miasma.", mh, NULL, NULL, TO_ALL);
		}
		SET_BIT(mh->act, ACT_SUMMONED);
		char_reset(mh);
		SET_BIT( mh->affected_by, AFF_DOMINATE );
		add_follower( mh , ch );
		--count;
	}

	pop_call();
	return TRUE;
}


/*
 * Fiendish and Celestial summoning ability - Kregor
 */
DO_SPELL(spell_summon_ally)
{
	MOB_INDEX_DATA *pMob;
	CHAR_DATA *mh;
	int race, hitdice, count, cnt, rdm;
	bool fMatch = FALSE;

	push_call("spell_summon_creature(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (level * 5 < number_percent())
	{
		act("Your summons failed.", ch, NULL, NULL, TO_CHAR);
		act("Noone answers $n's summons.", ch, NULL, NULL, TO_ROOM);
		pop_call();
		return TRUE;
	}

	for (cnt = race = 0 ; race < MAX_RACE ; race++)
	{
		if (race_table[race].type != race_table[ch->race].type)
			continue;
		if (race_table[race].flags != race_table[ch->race].flags)
			continue;
		if (race_table[race].hit_dice > level)
			continue;

		cnt++;
		fMatch = TRUE;
	}
	
	if (!fMatch)
	{
		act( "You can't seem to summon forth an ally.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	rdm = number_range(1, cnt);

	for (cnt = race = 0 ; race < MAX_RACE ; race++)
	{
		if (race_table[race].type != race_table[ch->race].type)
			continue;
		if (race_table[race].flags != race_table[ch->race].flags)
			continue;
		if (race_table[race].hit_dice > level)
			continue;

		if (cnt++ == rdm)
			break;
	}
	
	hitdice = race_table[race].hit_dice;
	count   = level / hitdice; 

	pMob = get_mob_index(MOB_VNUM_SUMMONS);

	while (count)
	{
		mh = create_mobile(pMob);
		char_to_room( mh, ch->in_room->vnum, TRUE );
	
		mh->race				= race;
		mh->level				= race_table[race].hit_dice;
		mh->perm_str		= 10 + race_table[race].race_mod[0];
		mh->perm_dex		= 10 + race_table[race].race_mod[1];
		mh->perm_con		= 10 + race_table[race].race_mod[2];
		mh->perm_int		= 10 + race_table[race].race_mod[3];
		mh->perm_wis		= 10 + race_table[race].race_mod[4];
		mh->perm_cha		= 10 + race_table[race].race_mod[5];
		mh->size				= race_table[race].size;
		mh->height			= race_table[race].height;
		mh->weight			= race_table[race].weight;
		mh->speak				= race_table[race].speaks;
		mh->language		= race_table[race].understands;
		mh->alignment		= race_table[race].alignment;
		mh->ethos				= race_table[race].ethos;
		
		mh->max_hit		= dice(mh->level, race_type_table[race_table[race].type].hit_die);
		mh->max_hit		= UMAX(mh->level * race_type_table[race_table[race].type].hit_die / 2, mh->max_hit);
		mh->hit				= get_max_hit(mh);
		
		RESTRING(mh->name, format("summoned %s", race_table[race].race_name));
		RESTRING(mh->short_descr, format("a summoned %s", race_table[race].race_name));
		RESTRING(mh->long_descr, format("A summoned %s is here.", race_table[race].race_name));
		mh->npcdata->sac_timer = level;
		
		if (!IS_EVIL(mh))
		{
			mh->npcdata->sac_string = STRALLOC("{178}$n shimmers and vanishes!");
			act( "{178}$n appears in a shimmer of light.", mh, NULL, NULL, TO_ALL);
		}
		else
		{
			mh->npcdata->sac_string = STRALLOC("{108}$n vanishes in a puff black smoke!");
			act( "{118}$n appears in a burst of hellish flames!", mh, NULL, NULL, TO_ALL);
		}
		SET_BIT(mh->act, ACT_SUMMONED);
		char_reset(mh);
		if (who_fighting(ch))
			fight(mh, who_fighting(ch));
		--count;
	}

	pop_call();
	return TRUE;
}


DO_SPELL(spell_conj_elemental)
{
	MOB_INDEX_DATA *pMob;
	AFFECT_DATA af;
	CHAR_DATA *mh;
	char arg[MAX_INPUT_LENGTH];
	int race, hitdice, mod, bloodline, class;

	push_call("spell_conj_elemental(%p,%p,%p,%p)",sn,level,ch,vo);

	hitdice = skill_table[sn].native_level * 4 / 3;
	hitdice = URANGE(hitdice - 1, level, hitdice + 1);
	
	if (sn == gsn_elemental_swarm)
		hitdice = hitdice / 4 * 3;
		
	class = ch->cast_class;

	if (sn == gsn_elemental_swarm || *target_name == '\0')
	{
		race = number_range(RACE_ELEMENTAL_AIR, RACE_ELEMENTAL_WATER);

		if (class == CLASS_CLERIC)
		{
			if (has_domain(ch, DOMAIN_AIR))
				race = RACE_ELEMENTAL_AIR;
			else if (has_domain(ch, DOMAIN_EARTH))
				race = RACE_ELEMENTAL_EARTH;
			else if (has_domain(ch, DOMAIN_FIRE))
				race = RACE_ELEMENTAL_FIRE;
			else if (has_domain(ch, DOMAIN_WATER))
				race = RACE_ELEMENTAL_WATER;
		}
		else if (class == CLASS_SORCERER)
		{
			if (get_bloodline(ch) == BLOODLINE_AIR)
				race = RACE_ELEMENTAL_AIR;
			else if (get_bloodline(ch) == BLOODLINE_EARTH)
				race = RACE_ELEMENTAL_EARTH;
			else if (get_bloodline(ch) == BLOODLINE_FIRE)
				race = RACE_ELEMENTAL_FIRE;
			else if (get_bloodline(ch) == BLOODLINE_WATER)
				race = RACE_ELEMENTAL_WATER;
		}
	}
	else
	{
		one_argument(target_name, arg);
		
		if (!strcasecmp(arg, "air"))
			race = RACE_ELEMENTAL_AIR;
		else if (!strcasecmp(arg, "earth"))
			race = RACE_ELEMENTAL_EARTH;
		else if (!strcasecmp(arg, "fire"))
			race = RACE_ELEMENTAL_FIRE;
		else if (!strcasecmp(arg, "water"))
			race = RACE_ELEMENTAL_WATER;
		else
		{
			send_to_char("That is not an element.\n\r", ch);
			pop_call();
			return FALSE;
		}
	}
	if (class == CLASS_CLERIC || class == CLASS_SORCERER)
	{
		if ((race == RACE_ELEMENTAL_AIR && !has_domain(ch, DOMAIN_AIR) && get_bloodline(ch) != BLOODLINE_AIR)
		|| (race == RACE_ELEMENTAL_EARTH && !has_domain(ch, DOMAIN_EARTH) && get_bloodline(ch) != BLOODLINE_EARTH)
		|| (race == RACE_ELEMENTAL_FIRE && !has_domain(ch, DOMAIN_FIRE) && get_bloodline(ch) != BLOODLINE_FIRE)
		|| (race == RACE_ELEMENTAL_WATER && !has_domain(ch, DOMAIN_WATER) && get_bloodline(ch) != BLOODLINE_WATER))
		{
			act("You have no control over that elemental domain.", ch, NULL, NULL, TO_CHAR);
			pop_call();
			return FALSE;
		}
	}
	
	if ((pMob = get_mob_index(MOB_VNUM_SUMMONS)) == NULL)
	{
		send_to_char( "You cannot create an elemental now.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	// sets up continuous affect to continue summoning
	if (sn == gsn_elemental_swarm && !is_affected(ch, gsn_elemental_swarm))
	{
		af.type				= sn;
		af.duration 	= turn * level;
		af.level			= level;
		af.bittype		= AFFECT_TO_NONE;
		af.bitvector 	= AFF_NONE;
		af.location		= APPLY_NONE;
		af.modifier		= dice(3,4);
		affect_to_char( ch, ch, &af );
		
		act("You open up a portal to the elemental planes.", ch, NULL, NULL, TO_CHAR);
		act("$n opens up a portal to the elemental planes.", ch, NULL, NULL, TO_ROOM);
	}

	mh = create_mobile( pMob );
	char_to_room( mh, ch->in_room->vnum, TRUE );
	mh->npcdata->sac_timer = turn * level;
	
	switch (hitdice)
	{
		case 1:
		case 2:
		case 3:
			mh->size = SIZE_SMALL;
			mod = 0;
			break;
		case 4:
		case 5:
		case 6:
			mh->size = SIZE_MEDIUM;
			mod = 1;
			break;
		case 7:
		case 8:
		case 9:
			mh->size = SIZE_LARGE;
			mod = 2;
			break;
		default:
			mh->size = SIZE_HUGE;
			mod = 3;
			break;
	}
			
	mh->race				= race;
	mh->level				= hitdice;
	mh->perm_str		= 10 + race_table[race].race_mod[0];
	mh->perm_dex		= 10 + race_table[race].race_mod[1];
	mh->perm_con		= 10 + race_table[race].race_mod[2];
	mh->perm_int		= 10 + race_table[race].race_mod[3];
	mh->perm_wis		= 10 + race_table[race].race_mod[4];
	mh->perm_cha		= 10 + race_table[race].race_mod[5];
	mh->height			= race_table[race].height;
	mh->weight			= race_table[race].weight;
	mh->speak				= race_table[race].speaks;
	mh->language		= race_table[race].understands;
	mh->alignment		= race_table[race].alignment;
	mh->ethos				= race_table[race].ethos;

	if (mod)
	{
		mh->height    *= 2 * mod;
		mh->weight    *= 8 * mod;
	}
	if (learned(ch, gsn_augment_summoning))
	{
		mh->perm_str += 4;
		mh->perm_con += 4;
	}
	
	mh->perm_str  	+= mod * 4;
	mh->perm_con  	+= mod * 2;
	if (race == RACE_ELEMENTAL_AIR)
		mh->perm_dex 	+= mod * 4;
	mh->max_hit			= dice(mh->level, race_type_table[race_table[mh->race].type].hit_die);
	mh->max_hit			= UMAX(mh->level * race_type_table[race_table[mh->race].type].hit_die / 2, mh->max_hit);
	mh->hit					= get_max_hit(mh);

	RESTRING(mh->name, format("summoned %s", race_table[race].race_name));
	RESTRING(mh->short_descr, format("a summoned %s", race_table[race].race_name));
	RESTRING(mh->long_descr, format("A summoned %s is here.", race_table[race].race_name));

	if (mh->size >= SIZE_LARGE)
	{
		af.type      = -1;
		af.duration  = -1;
		af.level     = level;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = AFF_NONE;
		af.location  = APPLY_DR_NONE;
		af.modifier  = mh->size == SIZE_LARGE ? 5 : 10;
		affect_to_char( mh, mh, &af );
	}
	
	SET_BIT( mh->affected_by , AFF_DOMINATE );

	if ((bloodline = get_bloodline(ch)) == BLOODLINE_CELESTIAL || bloodline == BLOODLINE_ABYSSAL)
	{
		AFFECT_DATA af;
		
		af.type      = sn;
		if (bloodline == BLOODLINE_ABYSSAL)
			af.location  = APPLY_DR_GOOD;
		else
			af.location  = APPLY_DR_EVIL;
		af.modifier  = UMAX(1, class_level(ch, CLASS_SORCERER) / 2);
		af.level     = level;
		af.duration  = -1;
		af.bittype   = AFFECT_TO_NONE;
		af.bitvector = AFF_NONE;
		affect_join( ch, mh, &af );
	}
			
	switch(race)
	{
		case RACE_ELEMENTAL_FIRE:
			act( "{118}A fiery humanoid forms from a swirl of flames.", mh, NULL, NULL, TO_ROOM );
			break;
		case RACE_ELEMENTAL_WATER:
			act( "{148}A swirl of water forms into a humanoid shape.", mh, NULL, NULL, TO_ROOM );
			break;
		case RACE_ELEMENTAL_AIR:
			act( "{168}A blast of wind swirls into a whispy figure.", mh, NULL, NULL, TO_ROOM );
			break;
		case RACE_ELEMENTAL_EARTH:
			act( "{038}A large creature of rock rises out of the ground.", mh, NULL, NULL, TO_ROOM );
			break;
	}
	add_follower( mh , ch );
	
	pop_call();
	return TRUE;
}


DO_SPELL(spell_contraption)
{
	MOB_INDEX_DATA *pMob;
	CHAR_DATA *mh;
	int race, hitdice, mod;

	push_call("spell_contraption(%p,%p,%p,%p)",sn,level,ch,vo);

	if (sn == gsn_major_contraption)
	{
		hitdice = level;
	}
	else
	{
		hitdice = skill_table[sn].native_level * 4 / 3;
		hitdice = URANGE(hitdice - 1, level, hitdice + 1);
	}	

	if (sn == gsn_major_contraption)
		race = RACE_GOLEM_CLOCKWORK;
	else
		race = RACE_ANIMATED_OBJ;
	
	if ((pMob = get_mob_index(MOB_VNUM_SUMMONS)) == NULL)
	{
		send_to_char( "You cannot seem to conjure your contraption.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	mh = create_mobile( pMob );
	char_to_room( mh, ch->in_room->vnum, TRUE );
	
	mh->npcdata->sac_timer = turn * level;
	
	if (sn == gsn_major_contraption)
	{
		mod = 0;
	}
	else
	{
		switch (hitdice)
		{
			case 1:
			case 2:
			case 3:
				mh->size = SIZE_SMALL;
				mod = 0;
				break;
			case 4:
			case 5:
			case 6:
				mh->size = SIZE_MEDIUM;
				mod = 1;
				break;
			case 7:
			case 8:
			case 9:
				mh->size = SIZE_LARGE;
				mod = 2;
				break;
			default:
				mh->size = SIZE_HUGE;
				mod = 3;
				break;
		}
	}
				
	mh->race				= race;
	mh->level				= hitdice;
	mh->perm_str		= 10 + race_table[race].race_mod[0];
	mh->perm_dex		= 10 + race_table[race].race_mod[1];
	mh->perm_con		= 10 + race_table[race].race_mod[2];
	mh->perm_int		= 10 + race_table[race].race_mod[3];
	mh->perm_wis		= 10 + race_table[race].race_mod[4];
	mh->perm_cha		= 10 + race_table[race].race_mod[5];
	mh->height			= race_table[race].height;
	mh->weight			= race_table[race].weight;
	mh->speak				= race_table[race].speaks;
	mh->language		= race_table[race].understands;
	mh->alignment		= race_table[race].alignment;
	mh->ethos				= race_table[race].ethos;

	if (mod)
	{
		mh->height    *= 2 * mod;
		mh->weight    *= 8 * mod;
	}
	if (learned(ch, gsn_augment_summoning))
	{
		mh->perm_str += 4;
		mh->perm_con += 4;
	}
	
	mh->perm_str  	+= mod * 4;
	mh->perm_con  	+= mod * 2;
	mh->max_hit			= dice(mh->level, race_type_table[race_table[mh->race].type].hit_die);
	mh->max_hit			= UMAX(mh->level * race_type_table[race_table[mh->race].type].hit_die / 2, mh->max_hit);
	mh->hit					= get_max_hit(mh);

	RESTRING(mh->name, format("clockwork golem %s m%s", size_types[get_size(mh)], pMob->vnum));
	RESTRING(mh->short_descr, format("a %s clockwork golem", size_types[get_size(mh)]));
	RESTRING(mh->long_descr, format("{038}A %s shambling clockwork golem wizzes and clanks.", size_types[get_size(mh)]));

	if (race == RACE_GOLEM_CLOCKWORK)
	{
		mh->learned[gsn_rock_throwing]++;
	}
	
	SET_BIT( mh->affected_by , AFF_DOMINATE );

	act( "{078}$n rises from the ground in a swirl of gears and cogs!", mh, NULL, NULL, TO_ROOM );

	add_follower( mh , ch );
	
	pop_call();
	return TRUE;
}


DO_SPELL(spell_contagion)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	DISEASE_DATA *dis, *dis_next;
	char buf1[MAX_STRING_LENGTH];
	char buf2[MAX_STRING_LENGTH];
	int disease;

	push_call("spell_contagion(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!is_string(target_name))
	{
		disease = DIS_BLINDING_SICKNESS;
	}
	else
	{
		two_arguments(target_name, buf1, buf2);
	
		if ((disease = lookup_disease(target_name)) == -1)
		{
			if ((disease = lookup_disease(buf2)) == -1)
			{
				if ((disease = lookup_disease(buf1)) == -1)
				{
					act("That is not a valid disease.", ch, NULL, NULL, TO_CHAR);
					pop_call();
					return FALSE;
				}
			}
		}
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (save_resist(ch, victim, sn, level))
	{
		pop_call();
		return TRUE;
	}
	
	infect_char(ch, victim, disease);

	for (dis = victim->first_disease ; dis ; dis = dis_next)
	{
		dis_next = dis->next;
		
		if (dis->type != disease)
			continue;
			
		if (dis->incubation < disease_table[dis->type].incubation)
		{
			dis->incubation = disease_table[dis->type].incubation;
			disease_update(victim);
			break;
		}
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_summon_shadow)
{
	MOB_INDEX_DATA *pMob;
	CHAR_DATA *mh;
	int roll, number, bloodline;

	push_call("spell_summon_shadow(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (roll = spell_dice(ch, sn, 1,3), number = 0 ; number < roll ; number++)
	{
		pMob = get_mob_index( 23 );
		mh = create_mobile( pMob );
		char_to_room( mh, ch->in_room->vnum, TRUE );
		SET_BIT(mh->affected_by , AFF_DOMINATE);
		SET_BIT(mh->act, ACT_SUMMONED);
		mh->npcdata->sac_timer 	= level;
		mh->npcdata->sac_string = STRALLOC("$n dissipates into swirling shadows.");

		if ((bloodline = get_bloodline(ch)) == BLOODLINE_CELESTIAL || bloodline == BLOODLINE_ABYSSAL)
		{
			AFFECT_DATA af;
			
			af.type      = sn;
			if (bloodline == BLOODLINE_ABYSSAL)
				af.location  = APPLY_DR_GOOD;
			else
				af.location  = APPLY_DR_EVIL;
			af.modifier  = UMAX(1, class_level(ch, CLASS_SORCERER) / 2);
			af.level     = level;
			af.duration  = -1;
			af.bittype   = AFFECT_TO_NONE;
			af.bitvector = AFF_NONE;
			affect_join( ch, mh, &af );
		}
			
		act( "A shadow appears out of a rift in the light.", ch, NULL, NULL, TO_CHAR);
		act( "A shadow appears out of a rift in the light.", ch, NULL, NULL, TO_ROOM);
	
		add_follower(mh, ch);
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_time_stop)
{
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;

	push_call("spell_time_stop(%p,%p,%p,%p)",sn,level,ch,vo);

	if (is_safe(ch, NULL))
	{
		act("You may not freeze time in this area.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next	= vch->prev_in_room;

		if (ch == vch)
			continue;
			
		af.type     	= sn;
		af.level    	= level;
		af.duration		= dice(1,4) + 1;
		af.bittype  	= AFFECT_TO_NONE;
		af.bitvector	= AFF_NONE;
		af.location 	= APPLY_NONE;
		af.modifier 	= 0;
		affect_join( ch, vch, &af);
			
		act( "{118}You stop in mid-action!", vch, NULL, NULL, TO_CHAR);
		act( "{118}$n freezes in mid-action!", vch, NULL, NULL, TO_ROOM);
	}
	pop_call();
	return TRUE;
}

DO_SPELL(spell_transmute_metal)
{
	OBJ_DATA *obj = (OBJ_DATA *) vo;
	AFFECT_DATA af;
	int value;

	push_call("spell_transmute_metal(%p,%p,%p,%p)",sn,level,ch,vo);

	if (material_table[obj->material].parent != MATERIAL_TYPE_METAL)
	{
		act( "You can only transmute metal to another metal.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!is_string(target_name))
	{
		act( "Transmute $p to what?", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if ((value = get_flag(target_name, material_types)) == -1
	|| material_table[value].parent != MATERIAL_TYPE_METAL)
	{
		act( "$t is not a valid type of metal.", ch, target_name, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (value == obj->material)
	{
		act( "$p is already that type of metal.", ch, obj, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "$p transmutes into $T!", ch, obj, material_types[value], TO_ALL);

	af.type      = sn;
	af.duration  = turn*level;
	af.location  = APPLY_MATERIAL;
	af.modifier  = value - obj->material;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.level     = level;
	affect_to_obj( ch,obj, &af);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_telepathic_bond)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch;
	AFFECT_DATA af;
	int cnt;

	push_call("spell_telepathic_bond(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for (cnt = 0, gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (!is_same_group(gch, victim))
			continue;

		if (is_affected(gch, sn))
			continue;

		if (gch->perm_int < 3)
			continue;

		if (cnt >= level / 3)
			break;

		af.type      = sn;
		af.location  = APPLY_NONE;
		af.duration  = level * hr;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF_TELEPATHY;
		af.level     = level;
		affect_to_char( ch, gch, &af);
		cnt++;

		act( "{138}You open a channel in your mind to $N.", ch, NULL, gch, TO_CHAR);
		act( "{138}You feel your mind attune to $n.", ch, NULL, gch, TO_VICT );
	}

	pop_call();
	return TRUE;
}

/*
 * REAL Teleport spell!
 * targets room names, has percent chance to go wrong,
 * PC can teleport to his recall point with no error.
 * Kregor - 6/29/07
 */
DO_SPELL(spell_teleport)
{    
	char dest[MAX_INPUT_LENGTH];
	int room, roll, point, attempts, sector, low_room, max_room;
	ROOM_INDEX_DATA *old_room;
	CHAR_DATA *rch, *rch_next;
	bool Mishap = FALSE;
	bool Random = FALSE;
	bool OffTarget = FALSE;
	 
	push_call("spell_teleport(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!is_string(target_name))
	{
		send_to_char( "Teleport where?\n\r", ch );
		pop_call();
		return FALSE;
	}
		
	target_name = one_argument(target_name, dest);
	roll = number_range(1,100);
	old_room = ch->in_room;
		
	if (!strcasecmp(dest, "waypoint"))
	{
		if (is_number(target_name))
		{
			point = atol(target_name);
			
			if ((room = ch->pcdata->waypoint[point]) <= 0)
			{
				send_to_char("You do not have that waypoint set.\n\r", ch);
				pop_call();
				return FALSE;
			}
			if (roll == 100)
				Random = TRUE;
			else if (roll >= 98)
				OffTarget = TRUE;
		}
		else
		{
			send_to_char("The waypoint must be a number.\n\r", ch);
			pop_call();
			return FALSE;
		}
	}	
	else if (!strcasecmp(dest, "home"))
	{
		if ((room = ch->pcdata->recall) <= 0)
		{
			send_to_char( "You do not have a recall point set.\n\r", ch);
			pop_call();
			return FALSE;
		}
	}
	else if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	else
	{
		ch_printf_color(ch, "You envision in your mind, '%s'.\n\r", dest);

		if (roll == 100)
			Mishap = TRUE;
		else if (roll >= 95)
			Random = TRUE;
		else if (roll >= 89)
			OffTarget = TRUE;			

		for (room = 200 ; room < MAX_VNUM  ; room++)
		{
			if (room == ch->in_room->vnum)
				continue;

			if (room_index[room] == NULL)
				continue;

			if (is_multi_name_list_short(dest, room_index[room]->name)
	 		&&  is_room_good_for_teleport(ch, room))
			{
				break;
			}
		}
	}

	if ( room == MAX_VNUM )
	{
		send_to_char_color("{138}The weave can't seem to find your destination.\n\r", ch);
		pop_call();
		return TRUE;
	}

	if (!is_room_good_for_teleport(ch, ch->in_room->vnum))
	{
		send_to_char( "Mystic wards prevent you from leaving.\n\r", ch );
		pop_call();
		return TRUE;
	}
	
	if (domain_apotheosis(ch, DOMAIN_TRAVEL))
	{
		Random = Mishap = OffTarget = FALSE;
	}
	
	if (Random || Mishap)
	{
		for (attempts = 0 ; attempts < 1000 ; attempts++)
		{	
			room = number_range(1, MAX_VNUM-1);

			if (room == ch->in_room->vnum)
				continue;

			if (room_index[room] == NULL)
				continue;

			if (is_room_good_for_teleport(ch, room))
			{
				break;
			}
		}
		if (Mishap)
		{
			send_to_char_color("{118}The weave scrambles you in your attempt!\n\r", ch);
			damage(ch, ch, dice(1, 10), TYPE_NOFIGHT, NULL);

			for (rch = old_room->first_person ; rch ; rch = rch_next)
			{
				rch_next = rch->next_in_room;
				if (is_master(rch, ch))
				{
					send_to_char_color("{118}The weave scrambles you in your teleport!\n\r", rch);
					damage(rch, rch, dice(1, 10), TYPE_NOFIGHT, NULL);
				}
			}
		}
		if (attempts >= 1000)
		{
			send_to_char_color("{138}The weave decides you should remain where you are.\n\r", ch);
			pop_call();
			return TRUE;
		}
		else if (!Mishap)
		{
			send_to_char_color("{138}Your attempt transported you somewhere else!\n\r", ch);
		}
	}
	if (OffTarget)
	{
		low_room = room_index[room]->area->low_r_vnum;
		max_room = room_index[room]->area->hi_r_vnum;
		sector = room_index[room]->sector_type;
		
		for (attempts = 0 ; attempts < 1000 ; attempts++)
		{	
			room = number_range(low_room, max_room);

			if (room == ch->in_room->vnum)
				continue;

			if (room_index[room] == NULL)
				continue;

			if (is_room_good_for_teleport(ch, room))
			{
				break;
			}
		}
		if (attempts >= 1000)
		{
			send_to_char_color("{138}The weave decides you should remain where you are.\n\r", ch);
			pop_call();
			return TRUE;
		}
		else
		{
			send_to_char_color("{138}The weave deposits you a little off from your target.\n\r", ch);
		}
	}

	act( "{138}$n disappears suddenly in a flash of light!", ch, NULL, NULL, TO_ROOM );

	if (in_combat(ch))
		withdraw_combat(ch);

	char_from_room( ch );
	char_to_room( ch, room, TRUE );

	act( "{138}$n arrives suddenly in a flash of light!", ch, NULL, NULL, TO_ROOM );
	act( "{138}You appear in a flash of light!", ch, NULL, NULL, TO_CHAR );
	do_look( ch, "auto" );

	for (rch = old_room->first_person ; rch ; rch = rch_next)
	{
		rch_next = rch->next_in_room;

		if (is_master(rch, ch))
		{
			act( "$n disappears suddenly in a flash of light.", rch, NULL, NULL, TO_ROOM);
			char_from_room(rch);
			char_to_room(rch, room, TRUE);
			act( "$n arrives suddenly in a flash of light.", rch, NULL, NULL, TO_ROOM);
			act( "{138}You appear in a flash of light!", rch, NULL, NULL, TO_CHAR );
			do_look(rch, "auto");
		}
	}
	if (!IS_NPC(ch))
	{
		mprog_greet_trigger(ch);
		oprog_greet_trigger(ch);
		rprog_greet_trigger(ch);
	}	
	pop_call();
	return TRUE;
}


/*
 * Greater teleport adds targeting characters, no misteleport,
 * group argument will teleport entire group - Kregor 10/20/07
 * added code support for Pass Plant, Shadow Walk - Kregor 2/4/11
 * merged in teleportation circle - Kregor 3/16/11
 */
DO_SPELL(spell_greater_teleport)
{    
	char dest[MAX_INPUT_LENGTH];
	char charmssg[MAX_INPUT_LENGTH];
	char bamfout[MAX_INPUT_LENGTH];
	char bamfin[MAX_INPUT_LENGTH];
	int room, point;
	ROOM_INDEX_DATA *old_room = NULL;
	ROOM_INDEX_DATA *to_room = NULL;
	OBJ_DATA *gate;
	CHAR_DATA *victim, *rch, *rch_next;
	bool fGroup = FALSE;
	 
	push_call("spell_greater_teleport(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!is_string(target_name))
	{
		send_to_char( "Teleport where?\n\r", ch );
		pop_call();
		return FALSE;
	}
		
	target_name = one_argument(target_name, dest);
	
	if (!strcasecmp(dest, "group"))
	{
		if (sn == gsn_dimension_door || sn == gsn_shadow_jump || sn == gsn_abundant_step || sn == gsn_earthwalk)
		{
			send_to_char("You cannot transport your group with this spell.\n\r", ch);
			pop_call();
			return FALSE;
		}
		fGroup = TRUE;
		target_name = one_argument(target_name, dest);
	}
	
	old_room = ch->in_room;
		
	if (!strcasecmp(dest, "waypoint"))
	{
		if (is_number(target_name))
		{
			point = atol(target_name);
			
			if ((room = ch->pcdata->waypoint[point]) <= 0)
			{
				send_to_char("You do not have that waypoint set.\n\r", ch);
				pop_call();
				return FALSE;
			}
		}
		else
		{
			send_to_char("The waypoint must be a number.\n\r", ch);
			pop_call();
			return FALSE;
		}
	}	
	else if (!strcasecmp(dest, "home"))
	{
		if ((room = ch->pcdata->recall) <= 0)
		{
			send_to_char( "You do not have a recall point set.\n\r", ch);
			pop_call();
			return FALSE;
		}
	}
	else if ((victim = get_char_world(ch, dest)) != NULL)
	{
		if ((to_room = victim->in_room) == NULL || !is_room_good_for_teleport(ch, to_room->vnum))
		{
			send_to_char( "That character can't be reached.\n\r", ch);
			pop_call();
			return FALSE;
		}
		room = victim->in_room->vnum;
	}
	else if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	else
	{
		ch_printf_color(ch, "You envision in your mind, '%s'.\n\r", dest);

		for (room = 200 ; room <= MAX_VNUM  ; room++)
		{
			if (room == ch->in_room->vnum)
				continue;

			if (room_index[room] == NULL)
				continue;
				
			if ((sn == gsn_dimension_door || sn == gsn_abundant_step || sn == gsn_shadow_jump) && room_index[room]->area != old_room->area)
				continue;

			if (is_multi_name_list_short(dest, room_index[room]->name)
	 		&&  is_room_good_for_teleport(ch, room))
			{
				break;
			}
		}
	}

	if (IS_SET(ch->in_room->room_flags, ROOM_NO_RECALL)
 	||	IS_SET(ch->in_room->area->flags, ROOM_NO_ASTRAL)
	||	IS_SET(ch->in_room->area->flags, AFLAG_NOTELEPORT)
	||	IS_SET(ch->in_room->area->flags, AFLAG_NORECALL)
	||	is_affected(ch, gsn_dimensional_anchor))
	{
		send_to_char( "Mystic energies prevent you from leaving.\n\r", ch );
		pop_call();
		return TRUE;
	}
	
	if ((to_room = get_room_index(room)) == NULL)
	{
		send_to_char_color("{138}The mystic energies can't seem to find your destination.\n\r", ch);
		pop_call();
		return TRUE;
	}
	if (!is_room_good_for_teleport(ch, room))
	{
		send_to_char_color("{138}The mystic energies can't seem to find your destination.\n\r", ch);
		pop_call();
		return TRUE;
	}
	
	if (sn == gsn_teleportation_circle)
	{
		gate = create_object(get_obj_index(OBJ_VNUM_GATE), 0);
	
		if (!gate)
		{
			ch_printf_color(ch,"You try to create a portal, but it does not seem possible somehow.\n\r");
			pop_call();
			return FALSE;
		}
		gate->value[0] 	= -1;
		gate->value[3] 	= to_room->vnum;
		gate->sac_timer = level * turn;
		obj_to_room(gate, ch->in_room->vnum);
	
		act( "$p opens before you.", ch, gate, NULL, TO_CHAR);
		act( "$p opens before $n.",  ch, gate, NULL, TO_ROOM);
	
		pop_call();
		return TRUE;
	}

	if (sn == gsn_shadow_walk || sn == gsn_shadow_jump)
	{
		if (mud->sunlight != SUN_DARK)
		{
			if (get_room_light(ch->in_room) >= LIGHT_NORMAL || get_room_light(to_room) >= LIGHT_NORMAL)
			{
				send_to_char_color("{108}It is not dark enough to walk the shadows to there.\n\r", ch);
				pop_call();
				return TRUE;
			}
		}
	}

	if (sn == gsn_pass_plant)
	{
		switch (ch->in_room->sector_type)
		{
			case SECT_FIELD:
			case SECT_FOREST:
			case SECT_HILLS:
			case SECT_SWAMP:
				break;
			default:
				send_to_char_color("{028}You cannot find suitable plant life to pass through.\n\r", ch);
				pop_call();
				return TRUE;
		}
		switch (to_room->sector_type)
		{
			case SECT_FIELD:
			case SECT_FOREST:
			case SECT_HILLS:
			case SECT_SWAMP:
				break;
			default:
				send_to_char_color("{028}You cannot find suitable plant life to pass through.\n\r", ch);
				pop_call();
				return TRUE;
		}
	}
	
	if (sn == gsn_earthwalk)
	{
		switch (ch->in_room->sector_type)
		{
			case SECT_MOUNTAIN:
			case SECT_UNDER_GROUND:
			case SECT_DEEP_EARTH:
			case SECT_TUNDRA:
			case SECT_BARREN:
				break;
			default:
				send_to_char_color("{028}You are not in stony terrain.\n\r", ch);
				pop_call();
				return TRUE;
		}
		switch (to_room->sector_type)
		{
			case SECT_MOUNTAIN:
			case SECT_UNDER_GROUND:
			case SECT_DEEP_EARTH:
			case SECT_TUNDRA:
			case SECT_BARREN:
				break;
			default:
				send_to_char_color("{028}You cannot find stony terrain in your destination.\n\r", ch);
				pop_call();
				return TRUE;
		}
	}
	
	if (sn == gsn_shadow_walk || sn == gsn_shadow_jump)
	{
		strcpy(charmssg, "{108}You emerge from the shadows!");
		strcpy(bamfin, "{108}$n swirls out of the shadows!");
		strcpy(bamfout, "{108}$n melts into the shadows!");
	}
	else if (sn == gsn_pass_plant)
	{
		strcpy(charmssg, "{028}You emerge from the foliage!");
		strcpy(bamfin, "{028}$n emerges from the foliage!");
		strcpy(bamfout, "{028}$n merges into the foliage!");
	}
	else if (sn == gsn_earthwalk)
	{
		strcpy(charmssg, "{038}You emerge from the earth!");
		strcpy(bamfin, "{038}$n forms out of earth!");
		strcpy(bamfout, "{038}$n melds into the earth!");
	}
	else if (sn == gsn_abundant_step)
	{
		strcpy(charmssg, "{188}You step into another space!");
		strcpy(bamfin, "{168}$n steps in out of nowhere!");
		strcpy(bamfout, "{168}$n seems to step into nowhere!");
	}
	else
	{
		strcpy(charmssg, "{138}You appear in a flash of light!");
		strcpy(bamfin, "{138}$n appears in a flash of light!");
		strcpy(bamfout, "{138}$n disappears in a flash of light!");
	}

	act( bamfout, ch, NULL, NULL, TO_ROOM );

	if (in_combat(ch))
		withdraw_combat(ch);

	char_from_room( ch );
	char_to_room( ch, room, TRUE );

	act( bamfin, ch, NULL, NULL, TO_ROOM );
	act( charmssg, ch, NULL, NULL, TO_CHAR );

	do_look( ch, "auto" );

	if (sn != gsn_shadow_jump)
	{
		for (rch = old_room->first_person ; rch ; rch = rch_next)
		{
			rch_next = rch->next_in_room;
	
			if (is_master(rch, ch) || (fGroup && is_same_group(rch, ch)))
			{
				act( bamfout, rch, NULL, NULL, TO_ROOM);
				char_from_room(rch);
				char_to_room(rch, room, TRUE);
				act( bamfin, rch, NULL, NULL, TO_ROOM);
				act( charmssg, rch, NULL, NULL, TO_CHAR );
				do_look(rch, "auto");
			}
		}
	}
	if (!IS_NPC(ch))
	{
		mprog_greet_trigger(ch);
		oprog_greet_trigger(ch);
		rprog_greet_trigger(ch);
	}	
	pop_call();
	return TRUE;
}


DO_SPELL(spell_teleport_object)
{
	CHAR_DATA *victim;
	OBJ_DATA *obj = (OBJ_DATA *) vo;

	push_call("spell_teleport_object(%p,%p,%p,%p)",sn,level,ch,vo);

	if ((victim = get_player_world(ch, target_name)) == NULL
	|| !is_room_good_for_teleport(ch, ch->in_room->vnum)
	|| !is_room_good_for_teleport(ch, victim->in_room->vnum))
	{
		send_to_char("Your cannot reach your target.\n\r", ch);
		pop_call();
		return TRUE;
	}

	if (victim == ch)
	{
		send_to_char("Transport it to yourself??", ch);
		pop_call();
		return FALSE;
	}

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

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	act( "$p {108}slowly dematerializes...", ch, obj, NULL, TO_CHAR );
	act( "$p {108}slowly dematerializes from $n's hands...", ch, obj, NULL, TO_ROOM );

	obj_from_char( obj );

	if (get_carry_w(victim) + get_obj_weight (obj) > can_carry_w(victim))
	{
		obj_to_room( obj, victim->in_room->vnum );
		act( "$p {138}from $n appears before you.", ch, obj, victim, TO_VICT);
		act( "$p {138}appears before $n!", victim, obj, NULL, TO_ROOM );
	}
	else
	{
		obj_to_char( obj, victim );
		act( "$p {138}from $n appears in your hands!", ch, obj, victim, TO_VICT);
		act( "$p {138}appears in $n's hands!", victim, obj, NULL, TO_ROOM );
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_tongues)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	AFFECT_DATA *paf, *paf_next;

	push_call("spell_tongues(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if ( is_affected(victim, sn) )
	{
		for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
		{
			paf_next = paf->next;
			if( paf->type == sn )
			{
				paf->duration  = hr * level;
			}
		}
		if (ch != victim)
			act( "{178}$n's spell of tongues is refreshed.", ch, NULL, victim, TO_CHAR );
		act( "{178}Your spell of tongues is refreshed.", victim, NULL, NULL, TO_CHAR );
		pop_call();
		return TRUE;
	}
	if ( IS_AFFECTED(victim, AFF_TONGUES))
	{
		send_to_char( "That person is already speaking in tongues.\n\r", ch);
		pop_call();
		return FALSE;
	}

	act( "{178}$n starts speaking in tongues.", victim, NULL, NULL, TO_ROOM);
	act( "{178}You start speaking in tongues.", victim, NULL, NULL, TO_CHAR);
	af.type      = sn;
	af.duration  = hr * level;
	af.location  = APPLY_NONE;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF_TONGUES;
	af.level     = level;
	affect_to_char( ch, victim, &af);

	pop_call();
	return TRUE;
}


DO_SPELL(spell_true_form)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA *paf, *paf_next;
	int paf_type, diceroll;

	push_call("spell_true_form(%p,%p,%p,%p)",sn,level,ch,vo);

	diceroll = dice(1, 20) + UMIN(level, 20);
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	for ( paf_type = 0, paf = victim->first_affect; paf != NULL; paf = paf_next )
	{
		paf_next = paf->next;

		if (!IS_SET(skill_table[paf->type].spell_desc, SDESC_POLYMORPH))
		{
			continue;
		}
		else if (is_spell(paf->type))
		{
			if (diceroll < 11 + paf->level)
				continue;
		}
		else
		{
			if (save_resist(ch, victim, sn, paf->level))
				continue;
		}
		paf_type = paf->type;
		revert_morph(victim, FALSE);
	}

	if (paf_type == 0)
	{
		send_to_char( "Nothing appears to happen.\n\r", ch );
	}
	else
	{
		act("$N morphs back into a $T.", victim, race_table[get_race(victim)].race_name, NULL, TO_ROOM);
		act("You morph back into a $T.", victim, race_table[get_race(victim)].race_name, NULL, TO_CHAR);
	}
	pop_call();
	return TRUE;
}


DO_SPELL(spell_vampiric_touch)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;
	int dam;

	push_call("spell_vampiric_touch(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	dam     = spell_dice(ch, sn, UMIN(level/2, 10), 6);
	dam			= UMIN(dam, ch->hit + 10);
	
	damage( ch, victim, dam, sn, NULL );

	af.type      = sn;
	af.duration  = hr;
	af.bittype   = AFFECT_TO_NONE;
	af.bitvector = 0;
	af.location  = APPLY_HIT;
	af.modifier  = dam;
	af.level     = level;
	affect_join( ch, ch, &af );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_ventriloquate)
{
	char buf1[MAX_STRING_LENGTH];
	char buf2[MAX_STRING_LENGTH];
	char text1[MAX_STRING_LENGTH];
	char text2[MAX_STRING_LENGTH];
	char speaker[MAX_INPUT_LENGTH];
	CHAR_DATA *vch, *victim;
	bool fVictim = FALSE;

	push_call("spell_ventriloquate(%p,%p,%p,%p)",sn,level,ch,vo);

	if( ch->in_room == NULL )
	{
		pop_call();
		return FALSE;
	}

	target_name = one_argument( target_name, speaker );

	if ((victim = get_char_room (ch, speaker)) != NULL)
	{
		fVictim = TRUE;
	}

	if (fVictim && victim->level >= LEVEL_IMMORTAL)
	{
		act("You cannot victimize $N.", ch, NULL, victim, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	strcpy(text2, target_name);
	
	sprintf(text1, "%s'%s'", get_color_string(ch, COLOR_SPEECH, VT102_DIM), text2);
	act( "You try to make $T say, $t", ch, text1, fVictim ? PERS(victim, ch) : speaker, TO_CHAR);

	for (text1[0] = '\0', vch = ch->in_room->first_person ; vch ; vch = vch->next_in_room)
	{
		if (vch == ch)
		{
			continue;
		}
		sprintf(text1, "%s'%s'", get_color_string(IS_NPC(ch) ? vch : ch, COLOR_SPEECH, VT102_DIM), text2);
		
		sprintf (buf1, "%s says, %s", fVictim ? PERS(victim, vch) : capitalize(speaker), text1);
		sprintf (buf2, "Someone nearby says, %s", text2);
	
		if (fVictim && vch == victim)
		{
			act( "Someone that sounds like you says, $t", victim, text1, fVictim ? PERS(victim, ch) : speaker, TO_CHAR);
		}
		else if (will_save(vch, ch, level, sn))
		{
			act( "$t", ch, buf2, vch, TO_VICT);
		}
		else
		{
			act( "$t", ch, buf1, vch, TO_VICT);
		}
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_wall)
{
	ROOM_TIMER_DATA rtd;
	EXIT_DATA *pExitData;
	int dir;

	push_call("spell_wall(%p,%p,%p,%p)",sn,level,ch,vo);

	if (IS_SET(ch->in_room->room_flags, ROOM_BLOCK))
	{
		send_to_char("This area is already blocked.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (IS_SET(ch->in_room->room_flags, ROOM_SAFE))
	{
		send_to_char( "Magical wards prevent your conjuration.\n\r", ch );
		pop_call();
		return FALSE;
	}

	if (!is_string(target_name) || (dir = direction_door(target_name)) == -1)
	{
		send_to_char("Block what direction?\n\r",ch);
		pop_call();
		return FALSE;
	}

	if ((pExitData = get_exit(ch->in_room->vnum, dir)) == NULL)
	{
		send_to_char("There is no exit in that direction!\n\r",ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	rtd.vnum			= ch->in_room->vnum;
	rtd.type			= sn;
	rtd.location	= APPLY_NONE;
	rtd.modifier	= dir;
	rtd.duration	= sn == gsn_wall_of_force ? level : sn == gsn_wall_of_iron ? -1 : turn * level;
	rtd.bitvector	= ROOM_BLOCK;
	rtd.level			= level;
	
	set_room_timer(ch, &rtd);

	if (sn == gsn_wall_of_stone)
	{
		act( "{108}A wall of stone springs up to the $t", ch, dir_name[dir], NULL, TO_CHAR);
		act( "{108}A wall of stone springs up to the $t", ch, dir_name[dir], NULL, TO_ROOM);
	}
	if (sn == gsn_wall_of_iron)
	{
		act( "{108}A wall of iron springs up to the $t", ch, dir_name[dir], NULL, TO_CHAR);
		act( "{108}A wall of iron springs up to the $t", ch, dir_name[dir], NULL, TO_ROOM);
	}
	if (sn == gsn_wall_of_ice)
	{
		act( "{168}A wall of ice springs up to the $t", ch, dir_name[dir], NULL, TO_CHAR);
		act( "{168}A wall of ice springs up to the $t", ch, dir_name[dir], NULL, TO_ROOM);
	}
	if (sn == gsn_wall_of_force)
	{
		act( "{168}You conjure wall of force to the $t", ch, dir_name[dir], NULL, TO_CHAR);
	}
	
	pop_call();
	return TRUE;
}


DO_SPELL(spell_water_breathing)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch;
	AFFECT_DATA af;
	int count;

	push_call("spell_water_breathing(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	for (count = 0, gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (!is_same_group(victim, gch))
		{
			continue;
		}
		count ++;
	}

	for (gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (!is_same_group(victim, gch))
		{
			continue;
		}
				
		af.type      = sn;
		af.duration  = (hr * level * 2) / count;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF_WATER_BREATH;
		af.level     = level;

		affect_to_char( ch, gch, &af );
		
		act( "{068}$N gains the ability to breathe water.", ch, NULL, gch, TO_CHAR );
		act( "{068}You gain the ability to breathe water.", gch, NULL, NULL, TO_CHAR );
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_water_walk)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *gch;
	AFFECT_DATA af;
	int count;

	push_call("spell_water_walk(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	for (count = 0, gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (!is_same_group(victim, gch))
		{
			continue;
		}
		count ++;
	}

	for (gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
	{
		if (!is_same_group(victim, gch))
		{
			continue;
		}
				
		af.type      = sn;
		af.duration  = (hr * level) / count;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF_WATER_WALK;
		af.level     = level;

		affect_to_char( ch, gch, &af );
		
		act( "{068}$N gains the ability to walk on water.", ch, NULL, gch, TO_CHAR );
		act( "{068}You gain the ability to walk on water.", gch, NULL, NULL, TO_CHAR );
	}

	pop_call();
	return TRUE;
}

DO_SPELL(spell_water_to_wine)
{
	OBJ_DATA *obj = (OBJ_DATA *)vo;
	char arg3[MAX_INPUT_LENGTH];
	int liquid;

	push_call("spell_water_to_wine(%p,%p,%p,%p)",sn,level,ch,vo);

	if (obj == NULL)
	{
		send_to_char("You are not carrying that.\n\r",ch);
		pop_call();
		return FALSE;
	}

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

	if (obj->item_type != ITEM_DRINK_CON)
	{
		send_to_char("That is not a drink container.\n\r",ch);
		pop_call();
		return FALSE;
	}

	if (!strlen(arg3))
	{
		send_to_char("Metamorphose to what kind of liquid?\n\r",ch);
		pop_call();
		return FALSE;
	}

	for (liquid = 0 ; liquid < LIQ_MAX ; liquid++)
	{
		if (!str_prefix(arg3, liq_table[liquid].liq_name))
		{
			break;
		}
	}

	if (liquid == LIQ_MAX)
	{
		send_to_char("There is no such liquid.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (obj->value[2] == liquid)
	{
		ch_printf_color(ch, "%s is already filled with %s\n\r", capitalize(obj->short_descr), liq_table[liquid].liq_name);
		pop_call();
		return TRUE;
	}
	else
	{
		ch_printf_color(ch, "You transform the %s in %s to %s.\n\r", liq_table[obj->value[2]].liq_name, obj->short_descr, liq_table[liquid].liq_name);
		obj->value[2] = liquid;
	}
	pop_call();
	return TRUE;
}

/*
 * Function for the Word of Faith alignment spells:
 * Blasphemy, Dictum, Holy Word and Word of Chaos
 * all pass thru this - Kregor
 */
DO_SPELL(spell_word_of_faith)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	CHAR_DATA *vch, *vch_next;
	AFFECT_DATA af;
	int vchsave, diff;
	char name[20];

	push_call("word_of_faith(%p,%p,%p,%p)",sn,level,ch,vo);
	
	if (!CAN_TALK(ch))
	{
		act("You are unable to utter a word of faith.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (sn == gsn_blasphemy)
	{
		act( "{138}$n rebukes the powers of good for $s god!", ch, NULL, NULL, TO_ROOM );
		act( "{138}You rebuke the powers of good for your god!", ch, NULL, NULL, TO_CHAR );
	}
	if (sn == gsn_dictum)
	{
		act( "{138}$n rebukes the powers of entropy for $s god!", ch, NULL, NULL, TO_ROOM );
		act( "{138}You rebuke the powers of entropy for your god!", ch, NULL, NULL, TO_CHAR );
	}
	if (sn == gsn_holy_word)
	{
		act( "{138}$n rebukes the powers of evil for $s god!", ch, NULL, NULL, TO_ROOM );
		act( "{138}You rebuke the powers of evil for your god!", ch, NULL, NULL, TO_CHAR );
	}
	if (sn == gsn_word_of_chaos)
	{
		act( "{138}$n rebukes the powers of law for $s god!", ch, NULL, NULL, TO_ROOM );
		act( "{138}You rebuke the powers of law for your god!", ch, NULL, NULL, TO_CHAR );
	}

	strcpy(name, skill_table[sn].noun_damage);

	for (vch = victim->in_room->last_person ; vch ; vch = vch_next)
	{
		vch_next = vch->prev_in_room;
		
		if (!can_mass_cast(ch, vch, sn))
			continue;

		if ((sn == gsn_blasphemy && IS_EVIL(vch))
		|| (sn == gsn_holy_word && IS_GOOD(vch))
		|| (sn == gsn_word_of_chaos && IS_CHAOTIC(vch))
		|| (sn == gsn_dictum && IS_LAWFUL(vch)))
			continue;

		if ((diff = level - vch->level) < 0)
		{
			act( "$N is unaffected by your $t.", ch, name, vch, TO_CHAR);
			act( "You are unaffected by $n's $t.", ch, name, vch, TO_VICT);
			act( "$N is unaffected by $N's $t.", ch, name, vch, TO_NOTVICT);
			continue;
		}
		
		if ((vchsave = save_resist(ch, vch, sn, level)) == TRUE)
			continue;
			
		if (IS_NPC(vch) && race_type(vch) == RTYPE_OUTSIDER && rspec_req(vch, RSPEC_EXTRAPLANAR))
		{
			act( "You are banished back to your home plane!", ch, name, vch, TO_VICT);
			act( "$N is banished back to $S home plane!", ch, name, vch, TO_ROOM);
			junk_mob(vch);
			continue;
		}
		
		if (diff >= 10)
		{
			if (vchsave == PARTIAL)
			{
				damage(ch, vch, dice(3,6)+UMIN(level,25), sn, NULL);

				if (!valid_fight(ch, vch))
					continue;
			}
			else
			{
				damage(ch, vch, ch->level * 10, sn, NULL);
				continue;
			}
		}
		if (diff >= 5)
		{
			af.type      = sn;
			af.duration  = vchsave ? 2 : dice(1,10)*turn;
			af.location  = 0;
			af.modifier  = 0;
			af.bittype   = AFFECT_TO_CHAR;
			af.bitvector = AFF2_PARALYSIS;
			af.level     = level;
			affect_join( ch, vch, &af );
		}
		af.type      = sn;
		af.duration  = vchsave ? 2 : dice(2,4);
		af.location  = 0;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		if (sn == gsn_blasphemy)
			af.bitvector = AFF2_SICKENED;
		else if (sn == gsn_word_of_chaos)
			af.bitvector = AFF2_CONFUSION;
		else if (sn == gsn_dictum)
			af.bitvector = AFF2_STAGGERED;
		else
			af.bitvector = AFF_BLIND;
		af.level     = level;
		affect_join( ch, vch, &af );
	}
	pop_call();
	return TRUE;
}


/*
 * Added group option for word of recall - Kregor
 */
DO_SPELL(spell_word_of_recall)
{
	CHAR_DATA *rch, *rch_next;
	ROOM_INDEX_DATA *old_room;
	int vnum;
	bool fGroup = FALSE;
	
	push_call("spell_word_of_recall(%p,%p,%p,%p)",sn,level,ch,vo);

	if (ch->in_room->vnum == ch->pcdata->recall)
	{
		send_to_char("You are already in your recall room.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (!is_room_good_for_teleport(ch, ch->in_room->vnum)
	|| !is_room_good_for_teleport(ch, ch->pcdata->recall))
	{
		act("You cannot reach that destination.", ch, NULL, NULL, TO_CHAR);
		pop_call();
		return FALSE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}
	
	if (!strcasecmp(target_name, "group"))
		fGroup = TRUE;
	
	if (ch->pcdata->recall == ROOM_VNUM_SCHOOL)
	{
		if (ch->in_room->area->low_r_vnum != ROOM_VNUM_SCHOOL)
		{
			ch->pcdata->recall = ROOM_VNUM_TEMPLE;
		}
	}

	if (ch->pcdata->recall < 3 || room_index[ch->pcdata->recall] == NULL)
	{
		ch->pcdata->recall = ROOM_VNUM_TEMPLE;
	}
	
	old_room = ch->in_room;
	vnum = ch->pcdata->recall;
	if (!recall(ch, ch->pcdata->recall))
	{
		pop_call();
		return TRUE;
	}
	
	if (fGroup)
	{
		for (rch = old_room->first_person ; rch ; rch = rch_next)
		{
			rch_next = rch->next_in_room;
	
			if (is_same_group(rch, ch))
			{
				recall(rch, vnum);
			}
		}
	}
	pop_call();
	return TRUE;
}


/*
 * Spell functions of currently unused spells
 */
DO_SPELL(spell_summon)
{
	CHAR_DATA *victim;

	push_call("spell_summon(%p,%p,%p,%p)",sn,level,ch,vo);

	if (target_name[0] == '\0')
	{
		send_to_char("Whom would you like to summon?\n\r", ch);
		pop_call();
		return FALSE;
	}

	if ((victim = get_char_area(ch, target_name)) == NULL)
	{
		send_to_char( "You cannot find anyone by that name.\n\r", ch);
		pop_call();
		return FALSE;
	}

	if (victim == ch || IS_NPC(victim))
	{
		send_to_char( "You can only summon other PCs.\n\r", ch );
		pop_call();
		return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	if (is_safe(ch, victim)
	|| !is_room_good_for_teleport(victim, ch->in_room->vnum)
	|| !is_room_good_for_teleport(victim, victim->in_room->vnum))
	{
		send_to_char( "You failed to summon your target.\n\r", ch );
		pop_call();
		return TRUE;
	}

	if (!is_same_group(ch, victim) && save_resist(ch, victim, sn, level))
	{
		act( "$N resists your summoning spell.", ch, NULL, NULL, TO_CHAR);
		act( "You feel a strange magical force pulling at you.", ch, NULL, victim, TO_VICT);

		pop_call();
		return TRUE;
	}

	act( "{138}$n disappears in a flash of light!", victim, NULL, NULL, TO_ROOM );
	act( "{138}You disappear suddenly.", victim, NULL, NULL, TO_CHAR );

	if (in_combat(victim))
		withdraw_combat(victim);
	char_from_room( victim );
	char_to_room( victim, ch->in_room->vnum, TRUE );

	act( "{138}$n arrives in a flash of light!", victim, NULL, NULL, TO_ROOM );
	act( "{138}You appear somewhere else, in a flash of light!", victim, NULL, NULL, TO_CHAR );

	do_look( victim, "auto" );

	pop_call();
	return TRUE;
}

DO_SPELL(spell_winged_call)
{
	CHAR_DATA *mob;

	push_call("spell_winged_call(%p,%p,%p,%p)",sn,level,ch,vo);

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	mob = create_mobile(mob_index[MOB_VNUM_WINGED_CALL]);

	char_to_room(mob, ch->in_room->vnum, TRUE);

	mob->level				= level / 2;
	mob->max_hit			= level * 5;
	mob->hit					= get_max_hit(mob);

	SET_BIT(mob->affected_by , AFF_DOMINATE);

	act( "You summon $N from the high mountains.", ch, NULL, mob, TO_CHAR);
	act( "$n summons $N from the high mountains.", ch, NULL, mob, TO_ROOM);

	add_follower(mob, ch);

	pop_call();
	return TRUE;
}

DO_SPELL(spell_possess)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

	push_call("spell_posses(%p,%p,%p,%p)",sn,level,ch,vo);

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

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

	if (victim == ch)
	{
		send_to_char("You already have control over yourself.\n\r", ch);
		pop_call();
		return FALSE;
	}

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

	if (!IS_NPC(victim) || !can_mass_cast(ch, victim, sn))
	{
		send_to_char("They seem to be unaffected by your magic.\n\r", ch);
		pop_call();
		return TRUE;
	}

	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	SET_BIT(victim->act,ACT_SENTINEL);
	REMOVE_BIT(victim->act, ACT_AGGRESSIVE);

	af.type      = sn;
	af.duration  = 12;
	af.location  = 0;
	af.modifier  = 0;
	af.bittype   = AFFECT_TO_CHAR;
	af.bitvector = AFF2_POSSESS;
	af.level     = level;
	affect_to_char( ch, victim, &af );

	log_printf("%s has possessed %s",get_name(ch), victim->short_descr);

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

	ch_printf_color(victim, "You have possessed %s.\n\r", get_name(victim));

	pop_call();
	return TRUE;
}

DO_SPELL(spell_totem)
{
	OBJ_DATA *totem;

	push_call("spell_totem(%p,%p,%p,%p)",sn,level,ch,vo);

	for (totem = ch->in_room->first_content ; totem ; totem = totem->next_content)
	{
		if (totem->item_type == ITEM_TOTEM)
		{
			send_to_char("There is already a totem here.\n\r", ch);
			pop_call();
			return FALSE;
		}
	}

	switch (ch->in_room->sector_type)
	{
		case SECT_AIR:
		case SECT_ETHEREAL:
		case SECT_ASTRAL:
		case SECT_LAKE:
		case SECT_RIVER:
		case SECT_OCEAN:
			send_to_char("You cannot cast a totem here.\n\r", ch);
			pop_call();
			return FALSE;
	}
	
	if (!ForReal)
	{
		pop_call();
		return TRUE;
	}

	totem = create_object( get_obj_index( OBJ_VNUM_TOTEM ), 0 );
	totem->timer = level;
	totem->level = level;

	act( "A totem rises up from the ground.", ch, NULL, NULL, TO_CHAR );
	act( "A totem rises up from the ground.", ch, NULL, NULL, TO_ROOM );

	obj_to_room( totem, ch->in_room->vnum );

	switch (tolower(target_name[0]))
	{
		case 'c':
			totem->value[0] = skill_lookup("neutralize poison");
			totem->value[1] = skill_lookup("remove blindness");
			totem->value[2] = skill_lookup("remove curse");
			totem->value[3] = skill_lookup("cure critical");
			break;

		case 'p':
			totem->value[0] = skill_lookup("bulls strength");
			totem->value[1] = skill_lookup("bears endurance");
			totem->value[2] = skill_lookup("bless");
			if (ch->alignment < 0)
			{
				totem->value[3] = skill_lookup("protection from good");
			}
			else
			{
				totem->value[3] = skill_lookup("protection from evil");
			}
			break;
		default:
			totem->value[0] = skill_lookup("heal");
			totem->value[1] = skill_lookup("cure light");
			totem->value[2] = skill_lookup("cure serious");
			totem->value[3] = skill_lookup("cure critical");
			break;
	}
	pop_call();
	return TRUE;
}