/***************************************************************************
* 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;
}