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