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 Johnson
 *																																				 *
 * 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.     *
 ***************************************************************************/

/***************************************************************************
 * race.c: Race specific functions																			   *
 ***************************************************************************/

#include "mud.h"

#define DO_ABILITY(ability) 			bool ability( int sn, int level, CHAR_DATA *ch, void *vo, int target )

#define turn		10
#define hr			100
#define PARTIAL -1

/*
 * Race table changed to load dynamically from race files - Kregor
 */
struct	race_type	race_table	[MAX_RACE];

/* Racial types ala D20 - Kregor 7/23/07 */
const	struct	race_type_type	race_type_table	[RTYPE_MAX]	=
{
	{
		"None", 0, 0, 
		DAM_NONE,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET|CAN_WEAR_SADDLE,
		SAVE_LOW, SAVE_LOW, SAVE_LOW, BAB_GOOD,
		FALSE, FALSE, FALSE, FALSE, FALSE
	},
	{
		"Aberration", 8, 4,
		DAM_NONE,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET|CAN_WEAR_SADDLE,
		SAVE_LOW, SAVE_LOW, SAVE_HIGH, BAB_GOOD,
		TRUE, TRUE, TRUE, TRUE, TRUE
	},
	{
		"Animal", 8, 2,
		DAM_NONE,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET|CAN_WEAR_SADDLE,
		SAVE_HIGH, SAVE_HIGH, SAVE_LOW, BAB_GOOD,
		TRUE, TRUE, TRUE, TRUE, TRUE
	},
	{
		"Construct", 10, 2,
		SDESC_FEAR|SDESC_DEATH|SDESC_ILLUSION|SDESC_MIND|SDESC_NEGATIVE|SDESC_PARALYSIS|SDESC_POISON|SDESC_SLEEP|SDESC_DISEASE|SDESC_DEATH|SDESC_HEALING,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET,
		SAVE_LOW, SAVE_LOW, SAVE_LOW, BAB_BEST,
		TRUE, FALSE, FALSE, FALSE, TRUE
	},
	{
		"Dragon", 12, 6,
		SDESC_PARALYSIS|SDESC_SLEEP,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET|CAN_WEAR_SADDLE,
		SAVE_HIGH, SAVE_HIGH, SAVE_HIGH, BAB_BEST,
		TRUE, TRUE, TRUE, TRUE, TRUE
	},
	{
		"Fey", 6, 6,
		DAM_NONE,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET,
		SAVE_LOW, SAVE_HIGH, SAVE_HIGH, BAB_POOR,
		FALSE, TRUE, TRUE, TRUE, TRUE
	},
	{
		"Humanoid", 8, 2,
		DAM_NONE,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET,
		SAVE_LOW, SAVE_HIGH, SAVE_LOW, BAB_GOOD,
		FALSE, TRUE, TRUE, TRUE, TRUE
	},
	{
		"Magical Beast", 10, 2,
		DAM_NONE,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET|CAN_WEAR_SADDLE,
		SAVE_HIGH, SAVE_HIGH, SAVE_LOW, BAB_BEST,
		FALSE, TRUE, TRUE, TRUE, TRUE
	},
	{
		"Monstrous Humanoid", 10, 4,
		DAM_NONE,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET,
		SAVE_LOW, SAVE_HIGH, SAVE_HIGH, BAB_BEST,
		FALSE, TRUE, TRUE, TRUE, TRUE
	},
	{
		"Ooze", 8, 2,
		SDESC_MIND|SDESC_ILLUSION|SDESC_FEAR|SDESC_LIGHT|SDESC_POISON|SDESC_SLEEP|SDESC_PARALYSIS|SDESC_POLYMORPH,
		DAM_NONE,
		CAN_WEAR_BODY|CAN_WEAR_ABOUT,
		SAVE_LOW, SAVE_LOW, SAVE_LOW, BAB_GOOD,
		TRUE, TRUE, TRUE, FALSE, FALSE
	},
	{
		"Outsider", 10, 6,
		DAM_NONE,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET,
		SAVE_LOW, SAVE_HIGH, SAVE_HIGH, BAB_BEST,
		FALSE, TRUE, TRUE, TRUE, TRUE
	},
	{
		"Plant", 8, 2,
		SDESC_MIND|SDESC_ILLUSION|SDESC_FEAR|SDESC_POISON|SDESC_SLEEP|SDESC_PARALYSIS|SDESC_POLYMORPH,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET,
		SAVE_HIGH, SAVE_LOW, SAVE_LOW, BAB_GOOD,
		FALSE, FALSE, TRUE, FALSE, TRUE
	},
	{
		"Undead", 8, 4,
		SDESC_DEATH|SDESC_FEAR|SDESC_DISEASE|SDESC_MIND|SDESC_ILLUSION|SDESC_POISON|SDESC_SLEEP|SDESC_PARALYSIS|SDESC_NEGATIVE,
		DAM_NONE,
		CAN_WEAR_HEAD|CAN_WEAR_FACE|CAN_WEAR_EARS|CAN_WEAR_NECK|CAN_WEAR_ARMS|CAN_WEAR_WRIST|CAN_WEAR_HANDS|CAN_WEAR_FINGER|CAN_WEAR_BODY|CAN_WEAR_ABOUT|CAN_WEAR_BACK|CAN_WEAR_WAIST|CAN_WEAR_LEGS|CAN_WEAR_ANKLE|CAN_WEAR_FEET,
		SAVE_LOW, SAVE_LOW, SAVE_HIGH, BAB_GOOD,
		TRUE, FALSE, FALSE, FALSE, TRUE
	},
	{
		"Vermin", 8, 2,
		SDESC_MIND|SDESC_FEAR|SDESC_ILLUSION,
		DAM_NONE,
		0,
		SAVE_HIGH, SAVE_LOW, SAVE_LOW, BAB_GOOD,
		TRUE, FALSE, FALSE, FALSE, TRUE
	}
};


int body_type( CHAR_DATA *ch )
{
	push_call("body_type(%p)",ch);
	
	if (IS_NPC(ch) && !ch->pIndexData->race)
	{
		pop_call();
		return ch->pIndexData->body_type;
	}

	pop_call();
	return race_table[get_race(ch)].body_type;
}

bool many_armed( CHAR_DATA *ch )
{
	push_call("many_armed(%p)",ch);
	
	switch (body_type(ch))
	{
		case BTYPE_MULTI_ARMED:
			pop_call();
			return TRUE;
	}
	pop_call();
	return FALSE;
}
	
bool many_legged( CHAR_DATA *ch )
{
	push_call("many_legs(%p)",ch);
	
	switch (body_type(ch))
	{
		case BTYPE_ARTHROPOD:
		case BTYPE_ARTHROPOD_HYBRID:
			pop_call();
			return TRUE;
	}
	pop_call();
	return FALSE;
}
	
bool quadruped( CHAR_DATA *ch )
{
	push_call("quadruped(%p)",ch);
	
	switch (body_type(ch))
	{
		case BTYPE_QUADRUPED:
		case BTYPE_QUADRUPED_HYBRID:
			pop_call();
			return TRUE;
	}
	pop_call();
	return FALSE;
}
	
bool can_trip( CHAR_DATA *ch )
{
	push_call("can_trip(%p)",ch);
	
	switch (body_type(ch))
	{
		case BTYPE_AMORPHOUS:
		case BTYPE_AQUATIC:
		case BTYPE_AVIAN:
		case BTYPE_RADIAL:
		case BTYPE_SERPENTINE:
		case BTYPE_SERPENTINE_HYBRID:
			pop_call();
			return TRUE;
	}
	pop_call();
	return FALSE;
}
	
/*
	What race type is ch
*/
int race_type( CHAR_DATA *ch )
{
	int type = RTYPE_NONE;
	
	push_call("race_type(%p)",ch);
	
	if (IS_NPC(ch) && !ch->pIndexData->race)
	{
		if ((type = ch->pIndexData->race_type) == RTYPE_NONE)
		{
			type = RTYPE_NONE;
		}
	}
	else
	{
		type = race_table[get_race(ch)].type;
	}

	if (type == RTYPE_ANIMAL && IS_ACT(ch, ACT_FAMILIAR))
		type = RTYPE_MAGICAL;

	if (type == RTYPE_ANIMAL && IS_ACT(ch, ACT_WARHORSE) && ch->level >= 11)
		type = RTYPE_MAGICAL;
		
	if (learned(ch, gsn_perfect_self) || learned(ch, gsn_divine_paragon))
		type = RTYPE_OUTSIDER;

	pop_call();
	return type;
}

/*
	Does ch posses a certain racial quality
*/
bool rspec_req( CHAR_DATA *ch, lg_int attr)
{
	push_call("rspec_req(%p,%p)",ch,attr);

	if (ch == NULL)
	{
		pop_call();
		return FALSE;
	}
	if (IS_NPC(ch) && !ch->race)
	{
		if (IS_SET(ch->pIndexData->rspecs, attr))
		{
			pop_call();
			return TRUE;
		}
	}
	if (IS_SET(race_table[get_race(ch)].flags, attr))
	{
		pop_call();
		return TRUE;
	}	
	// anyone under shapechange adopts shapechanger race type - Kregor
	if (attr == RSPEC_SHAPECHANGER && is_polymorph(ch))
	{
		pop_call();
		return TRUE;
	}
	// paladin 20th level or 20th good cleric = good subtype
	if (attr == RSPEC_GOOD && (domain_apotheosis(ch, DOMAIN_GOOD) || learned(ch, gsn_holy_champion)))
	{
		pop_call();
		return TRUE;
	}
	// blackguard 10th level or 20th evil cleric = evil subtype
	if (attr == RSPEC_EVIL && (domain_apotheosis(ch, DOMAIN_EVIL) || learned(ch, gsn_unholy_champion)))
	{
		pop_call();
		return TRUE;
	}
	if (attr == RSPEC_LAWFUL && domain_apotheosis(ch, DOMAIN_LAW))
	{
		pop_call();
		return TRUE;
	}
	if (attr == RSPEC_CHAOTIC && domain_apotheosis(ch, DOMAIN_CHAOS))
	{
		pop_call();
		return TRUE;
	}
	if (attr == RSPEC_AIR && (domain_apotheosis(ch, DOMAIN_AIR) || bloodline_capstone(ch, BLOODLINE_AIR)))
	{
		pop_call();
		return TRUE;
	}
	if (attr == RSPEC_FIRE && (domain_apotheosis(ch, DOMAIN_FIRE) || bloodline_capstone(ch, BLOODLINE_FIRE)))
	{
		pop_call();
		return TRUE;
	}
	if (attr == RSPEC_EARTH && (domain_apotheosis(ch, DOMAIN_EARTH) || bloodline_capstone(ch, BLOODLINE_EARTH)))
	{
		pop_call();
		return TRUE;
	}
	if (attr == RSPEC_WATER && (domain_apotheosis(ch, DOMAIN_WATER) || bloodline_capstone(ch, BLOODLINE_WATER)))
	{
		pop_call();
		return TRUE;
	}
	// monk 20th leveln or lawful 20th cleric = native lawful outsider
	if ((attr == RSPEC_LAWFUL || attr == RSPEC_NATIVE) && learned(ch, gsn_perfect_self))
	{
		pop_call();
		return TRUE;
	}
	// divine champion 10th = outsider
	if (learned(ch, gsn_divine_paragon))
	{
		if (attr == RSPEC_NATIVE)
		{
			pop_call();
			return TRUE;
		}
		if (attr == RSPEC_LAWFUL && IS_LAWFUL(ch) && god_table[ch->god].ethos == 1000)
		{
			pop_call();
			return TRUE;
		}
		if (attr == RSPEC_CHAOTIC && IS_CHAOTIC(ch) && god_table[ch->god].ethos == -1000)
		{
			pop_call();
			return TRUE;
		}
		if (attr == RSPEC_GOOD && IS_GOOD(ch) && god_table[ch->god].align == 1000)
		{
			pop_call();
			return TRUE;
		}
		if (attr == RSPEC_EVIL && IS_EVIL(ch) && god_table[ch->god].align == -1000)
		{
			pop_call();
			return TRUE;
		}
	}
	pop_call();
	return FALSE;
}

/*
 * is ch in an alternate form? - Kregor
 */
bool is_polymorph( CHAR_DATA *ch )
{
	push_call("is_poly(%p)",ch);

	if (ch->apply[APPLY_RACE])
	{
		pop_call();
		return TRUE;
	}
	if (get_race(ch) != ch->race)
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}

/*
 * checks for whether a char must eat or breathe
 */
bool must_eat(CHAR_DATA *ch)
{
	push_call("must_eat(%p)",ch);

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

	if (IS_AFFECTED(ch, AFF_SUSTAIN))
	{
		pop_call();
		return FALSE;
	}
	
	if (ch->level <= 1)
	{
		pop_call();
		return FALSE;
	}

	if (ch->level >= LEVEL_IMMORTAL)
	{
		pop_call();
		return FALSE;
	}
	
	if (IS_UNDEAD(ch))
	{
		pop_call();
		return FALSE;
	}

	if (ch->race != RACE_NONE)
	{
		pop_call();
		return race_type_table[race_table[get_race(ch)].type].eats;
	}
	pop_call();
	return TRUE;
}


bool must_breathe(CHAR_DATA *ch)
{
	push_call("must_breathe(%p)",ch);

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

	if (ch->level >= LEVEL_IMMORTAL)
	{
		pop_call();
		return FALSE;
	}
	
	if (IS_UNDEAD(ch))
	{
		pop_call();
		return FALSE;
	}
	
	if (is_affected(ch, gsn_iron_body))
	{
		pop_call();
		return FALSE;
	}

	if (ch->race != RACE_NONE)
	{
		pop_call();
		return race_type_table[race_table[get_race(ch)].type].breathes;
	}
	pop_call();
	return TRUE;
}


/*
 * function to return the age of a dragon
 * based on hit dice - Kregor
 */
int dragon_age( CHAR_DATA *ch )
{
	int race, level, age;
	
	push_call("dragon_age(%p)",ch);

	race = get_race(ch);
	
	if ((level = ch->level) <= race_table[race].hit_dice + 1)
	{
		pop_call();
		return DAGE_WYRMLING;
	}
	
	age = (level - race_table[race].hit_dice) / 2;
	age = UMIN(age, DAGE_GREAT_WYRM);
	
	pop_call();
	return age;
}

/*
 * Return whether a spell-like ability is known
 * based on the hit dice and race of dragon - Kregor
 */
bool dragon_spell( CHAR_DATA *ch, int sn )
{
	int age;
	
	push_call("dragon_spell(%p)",ch);
	
	age = dragon_age(ch);
	
	switch (ch->race)
	{
		case RACE_DRAGON_BLACK:
			if (sn == gsn_darkness && age >= DAGE_JUVENILLE)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_insect_plague && age == DAGE_ANCIENT)
			{
				pop_call();
				return 75;
			}
			break;
		case RACE_DRAGON_BLUE:
			if (sn == gsn_ventriloquism && age >= DAGE_JUVENILLE)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_major_image && age == DAGE_ADULT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_hallucinate && age == DAGE_OLD)
			{
				pop_call();
				return 75;
			}
			break;
		case RACE_DRAGON_GREEN:
			if (sn == gsn_suggestion && age >= DAGE_ADULT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_entangle && age == DAGE_OLD)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_dominate_person && age == DAGE_ANCIENT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_shambler && age == DAGE_GREAT_WYRM)
			{
				pop_call();
				return 75;
			}
			break;
		case RACE_DRAGON_RED:
			if (sn == gsn_locate_object && age >= DAGE_JUVENILLE)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_suggestion && age == DAGE_OLD)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_discern_location && age == DAGE_ANCIENT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_foresight && age == DAGE_GREAT_WYRM)
			{
				pop_call();
				return 75;
			}
			break;
		case RACE_DRAGON_WHITE:
			if (sn == gsn_fog_cloud && age >= DAGE_JUVENILLE)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_gust_of_wind && age == DAGE_ADULT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_ice_storm && age == DAGE_ANCIENT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_control_weather && age == DAGE_GREAT_WYRM)
			{
				pop_call();
				return 75;
			}
			break;
		case RACE_DRAGON_BRASS:
			if (sn == gsn_endure_elements && age >= DAGE_JUVENILLE)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_suggestion && age == DAGE_ADULT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_gust_of_wind && age == DAGE_OLD)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_sirocco && age == DAGE_ANCIENT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_control_weather && age == DAGE_GREAT_WYRM)
			{
				pop_call();
				return 75;
			}
			break;
		case RACE_DRAGON_BRONZE:
			if (sn == gsn_fog_cloud && age == DAGE_ADULT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_detect_thoughts && age == DAGE_OLD)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_control_weather && age == DAGE_GREAT_WYRM)
			{
				pop_call();
				return 75;
			}
			break;
		case RACE_DRAGON_COPPER:
			if (sn == gsn_earthwalk && age == DAGE_ADULT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_stone_shower && age == DAGE_OLD)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_repel_metal && age == DAGE_GREAT_WYRM)
			{
				pop_call();
				return 75;
			}
			break;
		case RACE_DRAGON_GOLD:
			if (sn == gsn_bless && age == DAGE_JUVENILLE)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_good_hope && age == DAGE_ADULT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_aura_of_retribution && age == DAGE_OLD)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_sunburst && age == DAGE_ANCIENT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_foresight && age == DAGE_GREAT_WYRM)
			{
				pop_call();
				return 75;
			}
			break;
		case RACE_DRAGON_SILVER:
			if (sn == gsn_feather_fall && age == DAGE_JUVENILLE)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_fog_cloud && age == DAGE_ADULT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_sirocco && age == DAGE_OLD)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_control_weather && age == DAGE_ANCIENT)
			{
				pop_call();
				return 75;
			}
			if (sn == gsn_storm_of_vengeance && age == DAGE_GREAT_WYRM)
			{
				pop_call();
				return 75;
			}
			break;
		default:
			break;		
	}
	
	pop_call();
	return FALSE;
}

/*
 * Return Sorcerer caster level of dragon
 * based on hit dice - Kregor
 */
int dragon_caster_level( int level )
{
	push_call("dragon_caster_level(%p)",level);
	
	if ((level -= 10) <= 0)
	{
		pop_call();
		return 0;
	}	
	pop_call();
	return level;
}

/*
 * Calculate SRD spell resistence for char - Kregor
 */
int spell_resist(CHAR_DATA *ch)
{
	int resist, apply; 

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

	if (ch->race == get_race(ch) && race_skill(ch, gsn_draconic_resistance)) // dragons have special rules
		resist = ch->level + 1;
	else if ((resist = race_table[ch->race].apply[APPLY_SPELL_RES]) > 0) // polymorph does not grant spell resist
		resist += ch->level;
	
	if ((apply = get_apply(ch, APPLY_SPELL_RES)) > resist)
		resist = apply;
		
	if (learned(ch, gsn_diamond_soul))
		resist = UMAX(resist, multi_class_level(ch, gsn_diamond_soul) + 10);

	pop_call();
	return resist;
}

/*
 * Is character vulnerable to a dam type?
 * Only one dam type need be true to be vulnerable - Kregor
 */
bool is_vulnerable( CHAR_DATA *ch, CHAR_DATA *victim, int dam_type )
{
	push_call("is_vulnerable(%p,%p,%p)",ch,victim,dam_type);
	
	if (IS_SET(race_table[get_race(victim)].vulnerability, dam_type))
	{
		pop_call();
		return TRUE;
	}
	if (IS_SET(race_type_table[race_table[get_race(victim)].type].vulnerability, dam_type))
	{
		pop_call();
		return TRUE;
	}

	pop_call();
	return FALSE;
}

/*
 * Calculate and cache the immunity to a spell descriptor - Kregor 11/19/09
 * Should be present in char_reset as well as effect_modify
 */
void apply_immunity_sdesc(CHAR_DATA * ch)	
{
	int mod = 0;
	int sn;
	
	push_call("apply_immunity_sdesc(%p)",ch);

	/* first apply from racial immunities */
	SET_BIT(mod, race_type_table[race_type(ch)].immunity);
	
	/* then applies */
	if (get_apply(ch, APPLY_IMM_AIR))
		SET_BIT(mod, SDESC_AIR);
	if (get_apply(ch, APPLY_IMM_CHAOTIC))
		SET_BIT(mod, SDESC_CHAOTIC);
	if (get_apply(ch, APPLY_IMM_DARKNESS))
		SET_BIT(mod, SDESC_DARKNESS);
	if (get_apply(ch, APPLY_IMM_DEATH))
		SET_BIT(mod, SDESC_DEATH);
	if (get_apply(ch, APPLY_IMM_DISEASE))
		SET_BIT(mod, SDESC_DISEASE);
	if (get_apply(ch, APPLY_IMM_EARTH))
		SET_BIT(mod, SDESC_EARTH);
	if (get_apply(ch, APPLY_IMM_EVIL))
		SET_BIT(mod, SDESC_EVIL);
	if (get_apply(ch, APPLY_IMM_FEAR))
		SET_BIT(mod, SDESC_FEAR);
	if (get_apply(ch, APPLY_IMM_FORCE))
		SET_BIT(mod, SDESC_FORCE);
	if (get_apply(ch, APPLY_IMM_GOOD))
		SET_BIT(mod, SDESC_GOOD);
	if (get_apply(ch, APPLY_IMM_ILLUSION))
		SET_BIT(mod, SDESC_ILLUSION);
	if (get_apply(ch, APPLY_IMM_LAWFUL))
		SET_BIT(mod, SDESC_LAWFUL);
	if (get_apply(ch, APPLY_IMM_LIGHT))
		SET_BIT(mod, SDESC_LIGHT);
	if (get_apply(ch, APPLY_IMM_MAGIC))
		SET_BIT(mod, SDESC_MAGIC);
	if (get_apply(ch, APPLY_IMM_MIND))
		SET_BIT(mod, SDESC_MIND);
	if (get_apply(ch, APPLY_IMM_NEGATIVE))
		SET_BIT(mod, SDESC_NEGATIVE);
	if (get_apply(ch, APPLY_IMM_PARALYSIS) || domain_apotheosis(ch, DOMAIN_LIBERATION))
		SET_BIT(mod, SDESC_PARALYSIS);
	if (get_apply(ch, APPLY_IMM_PETRI))
		SET_BIT(mod, SDESC_PETRI);
	if (get_apply(ch, APPLY_IMM_POISON))
		SET_BIT(mod, SDESC_POISON);
	if (get_apply(ch, APPLY_IMM_HEALING))
		SET_BIT(mod, SDESC_HEALING);
	if (get_apply(ch, APPLY_IMM_SLEEP))
		SET_BIT(mod, SDESC_SLEEP);
	if (get_apply(ch, APPLY_IMM_WATER))
		SET_BIT(mod, SDESC_WATER);
	
	/* then add sdesc immunities granted from abilities */
	for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
	{
		if (!learned(ch, sn))
			continue;

		if (skill_table[sn].skilltype != FSKILL_ABILITY)
			continue;

		if (!skill_table[sn].spell_desc)
			continue;
			
		if (skill_table[sn].skilltype == FSKILL_BARDSONG || IS_SET(skill_table[sn].flags, SF_SPELL_LIKE)) // whoops, need the sdesc here tho!
			continue;
			
		SET_BIT(mod, skill_table[sn].spell_desc);
	}
	
	ch->immunity_sdesc = mod;
	
	pop_call();
	return;
}

/*
 * is char immune to a certain spell descriptor
 */
bool is_immune( CHAR_DATA *victim, int sdesc )
{
	push_call("is_immune(%p,%p)",victim,sdesc);
	
	if (sdesc <= 0)
	{
		pop_call();
		return FALSE;
	}
	
	if (IS_SET(race_type_table[race_type(victim)].immunity, sdesc))
	{
		pop_call();
		return TRUE;
	}
	if (IS_SET(victim->immunity_sdesc, sdesc))
	{
		pop_call();
		return TRUE;
	}
	if (IS_SET(sdesc, SDESC_MIND) && get_curr_int(victim) < 0)
	{
		pop_call();
		return TRUE;
	}
	if (IS_SET(sdesc, SDESC_CHARM|SDESC_COMPULSION) && arcane_mastery(victim, SCHOOL_ENCHANTMENT))
	{
		pop_call();
		return TRUE;
	}
	if (IS_SET(sdesc, SDESC_FEAR) && domain_apotheosis(victim, DOMAIN_NOBILITY))
	{
		pop_call();
		return TRUE;
	}
	if (IS_SET(sdesc, SDESC_CHARM) && (domain_apotheosis(victim, DOMAIN_CHARM) || domain_apotheosis(victim, DOMAIN_SCALYKIND)))
	{
		pop_call();
		return TRUE;
	}
	if (IS_SET(sdesc, SDESC_POISON) && domain_apotheosis(victim, DOMAIN_SCALYKIND))
	{
		pop_call();
		return TRUE;
	}
	pop_call();
	return FALSE;
}

/*
 * Compute and cache the immunity to a damage type - Kregor 11/19/09
 * Should be present in char_reset as well as effect_modify
 */
void apply_immunity_energy(CHAR_DATA * ch)	
{
	int mod = 0;
	
	push_call("apply_immunity_energy(%p)",ch);
	
	if (get_apply(ch, APPLY_IMM_ACID))
		SET_BIT(mod, DAM_ACID);
	if (get_apply(ch, APPLY_IMM_COLD))
		SET_BIT(mod, DAM_COLD);
	if (get_apply(ch, APPLY_IMM_ELECTRIC) || (bloodline_capstone(ch, BLOODLINE_AIR)))
		SET_BIT(mod, DAM_ELECTRIC);
	if (get_apply(ch, APPLY_IMM_FIRE) || (bloodline_capstone(ch, BLOODLINE_FIRE)))
		SET_BIT(mod, DAM_FIRE);
	if (get_apply(ch, APPLY_IMM_SONIC))
		SET_BIT(mod, DAM_SONIC);

	ch->immunity_energy = mod;
	
	pop_call();
	return;
}

/*
 * Total immunity to an energy damage type.
 * Ablates immunity granted through spells.
 * checks all types to make sure all are resisted - Kregor
 */
int energy_immunity( CHAR_DATA *victim, int dam, int dam_type )
{
	AFFECT_DATA *paf, *paf_next;
	int cnt, i, j;
	int apply = 0;
	int immune;
	
	push_call("energy_immunity(%p,%p,%p)",victim,dam,dam_type);
	
	if (!IS_SET(dam_type, DAM_ENERGY)) // no energy damage = no resist
	{
		pop_call();
		return dam;
	}
	// damage with multiple energy types will still damage 
	// victim unless immune to all applicable energy forms
	for (i = j = 0, cnt = DAM_TYPE_ACID ; cnt < DAM_TYPE_MAX ; cnt++)
	{
		if (IS_SHIFT(dam_type, cnt) && IS_SHIFT(victim->immunity_energy, cnt))
			i++;
			
		if (IS_SHIFT(dam_type, cnt) && !IS_SHIFT(victim->immunity_energy, cnt))
			j++;
	}		
	if (j > 0) //means dam got thru that isn't immune stop here and damage victim.
	{
		pop_call();
		return dam;
	}
	if (i > 0) //means there was dam that was immune, check for ablation.
	{
		if (IS_SET(dam_type, DAM_ACID))
			apply = APPLY_IMM_ACID;
		if (IS_SET(dam_type, DAM_COLD))
			apply = APPLY_IMM_COLD;
		if (IS_SET(dam_type, DAM_ELECTRIC))
			apply = APPLY_IMM_ELECTRIC;
		if (IS_SET(dam_type, DAM_FIRE))
			apply = APPLY_IMM_FIRE;
		if (IS_SET(dam_type, DAM_SONIC))
			apply = APPLY_IMM_SONIC;
	
		if ((immune = victim->absorption[apply]) > 0)
		{
			if (dam < immune)
			{
				immune = dam;
				victim->absorption[apply] -= dam;
				dam = 0;
			}
			else
			{
				dam -= immune;
				victim->absorption[apply] = 0;
				
				for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
				{
					paf_next = paf->next;
					
					if (paf->location == apply)
					{
						if (paf->duration >= 0)
						{
							if (is_string(skill_table[paf->type].msg_off))
							{
								act( skill_table[paf->type].msg_off, victim, NULL, NULL, TO_CHAR);
							}
							if (is_string(skill_table[paf->type].msg_off_room))
							{
								act( skill_table[paf->type].msg_off_room, victim, NULL, NULL, TO_ROOM);
							}
						}
						affect_strip(victim, paf->type);
					}
				}
			}
		}
		else
		{
			immune = dam;
			dam = 0;
		}
		ch_printf_color(victim, "{138}Your immunity resists %d points of damage.\n\r", immune);
	}
		
	pop_call();
	return dam;
}

/*
 * Calculate the amount of damage passed thru
 * energy resistance.
 */
int energy_resistance( CHAR_DATA *victim, int dam, int dam_type )
{
	AFFECT_DATA *paf, *paf_next;
	int apply = 0;
	int bonus, resist;
	
	push_call("energy_resistance(%p,%p,%p)",victim,dam,dam_type);
	
	if (IS_SET(dam_type, DAM_ACID))
		apply = APPLY_DR_ACID;
	if (IS_SET(dam_type, DAM_COLD))
		apply = APPLY_DR_COLD;
	if (IS_SET(dam_type, DAM_ELECTRIC))
		apply = APPLY_DR_ELECTRIC;
	if (IS_SET(dam_type, DAM_FIRE))
		apply = APPLY_DR_FIRE;
	if (IS_SET(dam_type, DAM_SONIC))
		apply = APPLY_DR_SONIC;

	bonus = get_apply(victim, apply);

	if (dam <= 0)
	{
		pop_call();
		return bonus;
	}

	// if resistance was gained thru an ablative affect, reduce the cap here.
	if ((resist = victim->absorption[apply]) > 0)
	{
		if (dam < resist)
		{
			victim->absorption[apply] -= UMIN(dam, bonus);
		}
		else
		{
			victim->absorption[apply] = 0;
			
			for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
			{
				paf_next = paf->next;
				
				if (paf->location == apply)
				{
					if (paf->duration >= 0)
					{
						if (is_string(skill_table[paf->type].msg_off))
						{
							act( skill_table[paf->type].msg_off, victim, NULL, NULL, TO_CHAR);
						}
						if (is_string(skill_table[paf->type].msg_off_room))
						{
							act( skill_table[paf->type].msg_off_room, victim, NULL, NULL, TO_ROOM);
						}
					}
					affect_strip(victim, paf->type);
				}
			}
		}
	}
	if (bonus > 0)
		ch_printf_color(victim, "{138}Your energy resistance resists %d points of damage.\n\r", bonus);

	pop_call();
	return bonus;
}


/*
 * Calculate the amount of damage passed thru
 * physical damage reduction - Kregor
 * NOTE polymorph does not grant DR of assumed form
 */
int dam_reduction( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dam_type )
{
	int apply, resist;
	AFFECT_DATA *paf, *paf_next;
	
	push_call("dam_reduction(%p,%p,%p)",victim,dam,dam_type);
	
	resist = apply = 0;
	
	/* first, if it's energy damage, DR doesn't soak it */
	if (IS_SET(dam_type, DAM_ENERGY))
	{
		pop_call();
		return 0;
	}
	
	// swarms are resistant to physical attack - Kregor
	if (rspec_req(victim, RSPEC_SWARM))
	{
		if (victim->size < SIZE_TINY)
		{
			pop_call();
			return dam;
		}
		else if (IS_SET(dam_type, DAM_PIERCE|DAM_SLASH))
		{
			resist = dam / 2;
		}
	}
		
	/* next, remove nonlethal because it's irrelevant to DR */
	REMOVE_BIT(dam_type, DAM_NONLETHAL);
	
	/* then, compare the applies for DR against remaining dam types */
	if (get_apply(victim, APPLY_DR_NONE))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_NONE), resist);
		apply = APPLY_DR_NONE;
	}
	if (get_apply(victim, APPLY_DR_PIERCE) && !IS_SET(dam_type, DAM_PIERCE))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_PIERCE), resist);
		apply = APPLY_DR_PIERCE;
	}
	if (get_apply(victim, APPLY_DR_SLASH) && !IS_SET(dam_type, DAM_SLASH))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_SLASH), resist);
		apply = APPLY_DR_SLASH;
	}
	if (get_apply(victim, APPLY_DR_BASH) && !IS_SET(dam_type, DAM_BASH))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_BASH), resist);
		apply = APPLY_DR_BASH;
	}
	if (get_apply(victim, APPLY_DR_MAGIC) && !IS_SET(dam_type, DAM_MAGICAL))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_MAGIC), resist);
		apply = APPLY_DR_MAGIC;
	}
	if (get_apply(victim, APPLY_DR_GOOD) && !IS_SET(dam_type, DAM_GOOD))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_GOOD), resist);
		apply = APPLY_DR_GOOD;
	}
	if (get_apply(victim, APPLY_DR_EVIL) && !IS_SET(dam_type, DAM_EVIL))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_EVIL), resist);
		apply = APPLY_DR_EVIL;
	}
	if (get_apply(victim, APPLY_DR_LAW) && !IS_SET(dam_type, DAM_LAWFUL))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_LAW), resist);
		apply = APPLY_DR_LAW;
	}
	if (get_apply(victim, APPLY_DR_CHAOS) && !IS_SET(dam_type, DAM_CHAOTIC))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_CHAOS), resist);
		apply = APPLY_DR_CHAOS;
	}
	if (get_apply(victim, APPLY_DR_IRON) && !IS_SET(dam_type, DAM_IRON))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_IRON), resist);
		apply = APPLY_DR_IRON;
	}
	if (get_apply(victim, APPLY_DR_SILVER) && !IS_SET(dam_type, DAM_SILVER))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_SILVER), resist);
		apply = APPLY_DR_SILVER;
	}
	if (get_apply(victim, APPLY_DR_ADAMANTINE) && !IS_SET(dam_type, DAM_ADAMANTINE))
	{
		resist = UMAX(get_apply(victim, APPLY_DR_ADAMANTINE), resist);
		apply = APPLY_DR_ADAMANTINE;
	}
	
	// special dragon DR vs magic
	if (race_skill(victim, gsn_draconic_resistance) && !IS_SET(dam_type, DAM_MAGICAL))
	{
		int age = dragon_age(victim);
		
		if (age >= DAGE_WYRM)
		{
			resist = UMAX(20, resist);
			apply = APPLY_DR_MAGIC;
		}
		else if (age >= DAGE_VERY_OLD)
		{
			resist = UMAX(15, resist);
			apply = APPLY_DR_MAGIC;
		}
		else if (age >= DAGE_MATURE_ADULT)
		{
			resist = UMAX(10, resist);
			apply = APPLY_DR_MAGIC;
		}
		else
		{
			resist = UMAX(5, resist);
			apply = APPLY_DR_MAGIC;
		}
	}
	
	if (domain_apotheosis(victim, DOMAIN_RETRIBUTION) && ch != NULL && god_table[victim->god].faith_enemy[ch->god])
	{	
		resist = UMAX(5, resist);
		apply = APPLY_NONE;
	}
		
	if (dam <= 0)
	{
		pop_call();
		return resist;
	}
	
	// ablate the DR given by spells
	if (resist)
	{
		if (apply && victim->absorption[apply] > 0)
		{
			if (dam < resist)
			{
				victim->absorption[apply] -= dam;
				resist = dam;
			}
			else
			{
				victim->absorption[apply] -= resist;
			}
			if (victim->absorption[apply] <= 0)
			{			
				for ( paf = victim->first_affect; paf != NULL; paf = paf_next )
				{
					paf_next = paf->next;
					
					if (paf->location == apply)
					{
						if (paf->duration >= 0)
						{
							if (is_string(skill_table[paf->type].msg_off))
							{
								act( skill_table[paf->type].msg_off, victim, NULL, NULL, TO_CHAR);
							}
							if (is_string(skill_table[paf->type].msg_off_room))
							{
								act( skill_table[paf->type].msg_off_room, victim, NULL, NULL, TO_ROOM);
							}
						}
						affect_strip(victim, paf->type);
					}
				}
			}
		}
		else if (dam < resist)
		{
			resist = dam;
		}
		ch_printf_color(victim, "{138}Your damage reduction soaked %d points of damage.\n\r", resist);
	}
	pop_call();
	return resist;
}


/*
 * Does ch have a wear location? - Kregor
 */
bool has_body_part( CHAR_DATA *ch, lg_int part )
{
	push_call("has_body_part(%p)",ch);
	
	if (IS_NPC(ch) && !ch->race)
	{
		if (IS_SET(ch->pIndexData->wear_locs, part))
		{
			pop_call();
			return TRUE;
		}
	}
	else if (get_race(ch) > RACE_NONE)
	{
		if (IS_SET(race_table[get_race(ch)].wear_locs, part))
		{
			pop_call();
			return TRUE;
		}
	}
	pop_call();
	return FALSE;
}


/*
 * Calc racial skills - Kregor
 * skills = skill ranks
 * spells = daily uses for spell like ability
 * abilities = gained at X level
 */
int race_skill( CHAR_DATA *ch, int sn )
{
	int bonus = 0;
	
	push_call("race_skill(%p,%p)",ch,sn);
	
	if (sn == -1)
	{
		pop_call();
		return 0;
	}
	
	// if custom mobile, use hard-set skills - Kregor
	if (ch->race == RACE_NONE)
	{
		pop_call();
		return ch->learned[sn];
	}
	
	switch (skill_table[sn].skilltype)
	{
		default:
			bonus = skill_table[sn].race_skill[ch->race];
			break;
		case FSKILL_BARDSONG:
			bonus = 0;
			break;
		case FSKILL_RACEATTACK:
			if (sn == gsn_frightful_presence && IS_RACE(ch, RACE_DRAGON))
			{
				if (dragon_age(ch) >= DAGE_YOUNG_ADULT)
				{
					pop_call();
					return TRUE;
				}
				else
				{
					pop_call();
					return FALSE;
				}
			}	
			else if (IS_SET(skill_table[sn].flags, SF_EXTRAORDINARY))
			{
				bonus = skill_table[sn].race_skill[get_race(ch)];
			}
			else
			{
				bonus = skill_table[sn].race_skill[ch->race];
			}
			bonus = UMAX(bonus, ch->learned[sn]);
			break;
		case FSKILL_ABILITY:
		case FSKILL_FEAT:
			if (IS_SET(skill_table[sn].flags, SF_EXTRAORDINARY))
			{
				bonus = skill_table[sn].race_skill[get_race(ch)];
			}
			else
			{
				bonus = skill_table[sn].race_skill[ch->race];
			}
			if (ch->level < bonus)
			{
				bonus = 0;
			}
			break;
		case FSKILL_SPELL:
			if (dragon_spell(ch, sn))
			{
				pop_call();
				return TRUE;
			}
			bonus = skill_table[sn].race_skill[ch->race];
			break;
	}	
	pop_call();
	return bonus;
}


/*
 * racial attack ability funs - Kregor
 */
 
DO_ABILITY(ability_web_attack)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
	AFFECT_DATA af;

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

	if (is_safe_magic(ch, victim, sn))
	{
		pop_call();
		return FALSE;
	}
	
	act("You spray $N with strands of sticky webs!", ch, NULL, victim, TO_CHAR);
	act("$n sprays $N with strands of sticky webs!", ch, NULL, victim, TO_NOTVICT);
	act("$n sprays you with strands of sticky webs!", ch, NULL, victim, TO_VICT);

	if (!save_resist(ch, victim, sn, level))
	{	
		af.type      = sn;
		af.duration  = -1;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bittype   = AFFECT_TO_CHAR;
		af.bitvector = AFF2_ENTANGLED;
		af.level     = level;
		affect_join( ch, victim, &af );
	}
	pop_call();
	return TRUE;
}