/*************************************************************************** * Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming * * License by Wizards of the Coast. All comments referring to D20, OGL, * * and SRD refer to the System Reference Document for the Open Gaming * * system. Any inclusion of these derivatives must include credit to the * * Mud20 system, the full and complete Open Gaming LIcense, and credit to * * the respective authors. See ../doc/srd.txt for more information. * * * * Emud 2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem. * * * * MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey * * * * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * Original Diku Mud copyright (C) 1990 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeld, Tom Madsen, and Katje Nyboe. * ***************************************************************************/ /*************************************************************************** * character.c - broken off from handler to deal with character-specific * * handling functions - Kregor * ***************************************************************************/ #include "mud.h" #include <math.h> /* * Compute current stat of a character's abilities - Kregor. */ int get_curr_str( CHAR_DATA *ch ) { int stat; push_call("get_curr_str(%p)",ch); // nonabilities return as -1 if ((stat = ch->perm_str) <= 0 || rspec_req(ch, RSPEC_INCORPOREAL)) { pop_call(); return -1; } if (race_table[get_race(ch)].nonability[STAT_STR]) { pop_call(); return -1; } stat += ch->mod_str; // size bonus/penalty stat += get_apply(ch, APPLY_SIZE) * 2; stat += get_apply(ch, APPLY_STR); /* Per SRD, stat penalties for exaustion */ if (IS_EXHAUSTED(ch)) { stat -= 6; } else if (IS_FATIGUED(ch)) { stat -= 2; } // none of the above can lower stat past 1 stat = UMAX(1, stat); stat += get_apply(ch, APPLY_STR_DAMAGE); stat += get_apply(ch, APPLY_STR_DRAIN); pop_call(); return (UMAX(0, stat)); } int get_curr_int( CHAR_DATA *ch ) { int stat; push_call("get_curr_int(%p)",ch); if ((stat = ch->perm_int) <= 0) { pop_call(); return -1; } if (race_table[get_race(ch)].nonability[STAT_INT]) { pop_call(); return -1; } stat += ch->mod_int; stat += get_apply(ch, APPLY_INT); stat = UMAX(1, stat); stat += get_apply(ch, APPLY_INT_DAMAGE); stat += get_apply(ch, APPLY_INT_DRAIN); pop_call(); return (UMAX(0, stat)); } int get_curr_wis( CHAR_DATA *ch ) { int stat; push_call("get_curr_wis(%p)",ch); if ((stat = ch->perm_wis) <= 0) { pop_call(); return -1; } if (race_table[get_race(ch)].nonability[STAT_WIS]) { pop_call(); return -1; } stat += ch->mod_wis; stat += get_apply(ch, APPLY_WIS); stat = UMAX(1,stat); stat += get_apply(ch, APPLY_WIS_DAMAGE); stat += get_apply(ch, APPLY_WIS_DRAIN); pop_call(); return (UMAX(0, stat)); } /* * Retrieve character's current dexterity. */ int get_curr_dex( CHAR_DATA *ch ) { int stat; push_call("get_curr_dex(%p)",ch); if ((stat = ch->perm_dex) <= 0) { pop_call(); return -1; } if (race_table[get_race(ch)].nonability[STAT_DEX]) { pop_call(); return -1; } if (ch->position <= POS_SLEEPING || (IS_AFFECTED(ch, AFF2_PARALYSIS) && !IS_AFFECTED(ch, AFF_FREEDOM)) || drunk_level(ch) >= DRUNK_TOTALLED) { pop_call(); return 1; } stat += ch->mod_dex; stat -= get_apply(ch, APPLY_SIZE) * 2; if (is_affected(ch, gsn_iron_body)) stat -= 6; stat += get_apply(ch, APPLY_DEX); /* Per SRD, stat penalties for exaustion or entanglement * only the worst penalty applies. */ if (IS_EXHAUSTED(ch)) { stat -= 6; } else if (IS_ENTANGLED(ch)) { stat -= 4; } else if (IS_FATIGUED(ch)) { stat -= 2; } // none of the above reduce below 1 stat = UMAX(1,stat); stat += get_apply(ch, APPLY_DEX_DAMAGE); stat += get_apply(ch, APPLY_DEX_DRAIN); pop_call(); return (UMAX(0, stat)); } int get_curr_con( CHAR_DATA *ch ) { int stat; push_call("get_curr_con(%p)",ch); if ((stat = ch->perm_con) <= 0 || IS_UNDEAD(ch)) { pop_call(); return -1; } if (race_table[get_race(ch)].nonability[STAT_CON]) { pop_call(); return -1; } stat += ch->mod_con; stat += get_apply(ch, APPLY_CON); stat = UMAX(1, stat); stat += get_apply(ch, APPLY_CON_DAMAGE); stat += get_apply(ch, APPLY_CON_DRAIN); pop_call(); return (UMAX(0, stat)); } int get_curr_cha( CHAR_DATA *ch ) { int stat; push_call("get_curr_cha(%p)",ch); if ((stat = ch->perm_cha) <= 0) { pop_call(); return -1; } if (race_table[get_race(ch)].nonability[STAT_CHA]) { pop_call(); return -1; } stat += ch->mod_cha; stat += get_apply(ch, APPLY_CHA); stat = UMAX(1, stat); stat += get_apply(ch, APPLY_CHA_DAMAGE); stat += get_apply(ch, APPLY_CHA_DRAIN); pop_call(); return (UMAX(0, stat)); } /* * Get stat bonus - Based on SRD - Kregor */ int stat_bonus(bool fCurrent, CHAR_DATA *ch, int stat) { int value, bonus; push_call("stat_bonus(%p,%p,%p)",fCurrent,ch,stat); switch(stat) { case APPLY_STR: if (IS_INCORPOREAL(ch)) { value = ch->perm_dex; } else if (!fCurrent) { value = ch->perm_str; } else { value = get_curr_str(ch); } break; case APPLY_DEX: if (!fCurrent) value = ch->perm_dex; else value = get_curr_dex(ch); break; case APPLY_CON: if (IS_UNDEAD(ch) || race_type(ch) == RTYPE_CONSTRUCT) { value = 10; } else if (!fCurrent) { value = ch->perm_con; } else { value = get_curr_con(ch); } break; case APPLY_INT: if ((value = get_curr_int(ch)) < 0) value = 10; else if (!fCurrent) value = ch->perm_int; break; case APPLY_WIS: if (!fCurrent) value = ch->perm_wis; else value = get_curr_wis(ch); break; case APPLY_CHA: if (!fCurrent) value = ch->perm_cha; else value = get_curr_cha(ch); break; } if (value < 0) { pop_call(); return 0; } bonus = ((int)(value / 2)) - 5; pop_call(); return bonus; } /* * Reputation functions - Kregor */ int get_reputation( CHAR_DATA *ch ) { int value; push_call("get_reputation(%p)",ch); if (IS_NPC(ch)) { pop_call(); return 10; } value = ch->pcdata->reputation; if (IS_LAWFUL(ch)) value += 1; if (IS_CHAOTIC(ch)) value -= 1; if (IS_GOOD(ch)) value += 1; if (IS_EVIL(ch)) value -= 1; pop_call(); return UMAX(1,value); } int reputation_bonus( CHAR_DATA *ch ) { int value, bonus; push_call("reputation_bonus(%p)",ch); value = get_reputation(ch); bonus = ((int)(value / 2)) - 5; pop_call(); return bonus; } void gain_reputation( CHAR_DATA *ch, int amount ) { int value; push_call("gain_reputation(%p)",ch); if (IS_NPC(ch)) { pop_call(); return; } if (ch->pcdata->reputation + amount < 0) value = 0 - ch->pcdata->reputation; else value = amount; if (value < 0) { ch->pcdata->reputation = UMAX(0, ch->pcdata->reputation + value); ch_printf_color(ch, "{018}Word of your deeds costs you %d point%s of Reputation\n\r", value, value == -1 ? "" : "s"); } else { ch->pcdata->reputation += value; ch_printf_color(ch, "{138}Word of your deeds gains you %d point%s of Reputation\n\r", value, value == 1 ? "" : "s"); } pop_call(); return; } /* * Returns race number for shapechange/poly chars * for things that shapechange/poly should NOT apply, * just use ch->race - Kregor */ int get_race(CHAR_DATA * ch) { int race; push_call("get_race(%p)",ch); if (IS_NPC(ch) && !ch->pIndexData->race) { pop_call(); return RACE_NONE; } race = ch->race; race += get_apply(ch, APPLY_RACE); pop_call(); return (race); } /* * Return a string for the faith rank of a ch. * Return generic string if none set for deity, * or None if agnostic - Kregor */ char *faith_rank(CHAR_DATA *ch) { static char title[MAX_INPUT_LENGTH]; if (!ch->god) strcpy(title, "none"); else if (*god_table[ch->god].ranking[ch->pcdata->faith_rank] != '\0') strcpy(title, god_table[ch->god].ranking[ch->pcdata->faith_rank]); else strcpy(title, faith_ranks[ch->pcdata->faith_rank]); return title; } /* * Stat rolls and bonuses for SRD stat checks - Kregor */ int stat_mods( CHAR_DATA *ch, int stat ) { int mod, enc; push_call("stat_mods(%p)",ch); mod = stat_bonus(TRUE, ch, stat); enc = encumberance(ch) * 3; switch(stat) { default: break; case APPLY_STR: mod -= enc; if (learned(ch, gsn_divine_strength)) mod += 1; break; case APPLY_DEX: mod -= enc; if (IS_AFFECTED( ch, AFF2_FASCINATED)) mod -= 4; break; case APPLY_CHA: if (learned(ch, gsn_divine_charm)) mod += 1; break; case APPLY_WIS: if (IS_AFFECTED( ch, AFF2_FASCINATED)) mod -= 4; break; case APPLY_CON: if (learned(ch, gsn_endurance)) mod += 1; break; } mod += get_apply(ch, APPLY_COMP_SKILL); mod += get_apply(ch, APPLY_LUCK_SKILL); mod += get_apply(ch, APPLY_INS_SKILL); mod += get_apply(ch, APPLY_MOR_SKILL); mod += get_apply(ch, APPLY_LEVEL); if (IS_AFFECTED(ch, AFF2_SICKENED)) mod -= 2; if (ch->fear_level > 0) mod -= UMIN(ch->fear_level, 3) * 2; switch(drunk_level(ch)) { case DRUNK_SOBER: break; case DRUNK_TIPSY: mod -= 1; break; case DRUNK_MERRY: mod -= 2; break; case DRUNK_DRUNK: mod -= 4; break; case DRUNK_HAMMERED: mod -= 8; break; default: mod -= 16; break; } pop_call(); return( mod ); } /* * This variant rule allows for "skill-less" skill checks * for stat checks by adding a modifier based on class levels * if the stat is prime or secondary attribute for the class - Kregor */ int class_stat_bonus( CHAR_DATA *ch, int stat ) { int bonus, cnt; push_call("class_stat_bonus(%p)",ch); for (bonus = cnt = 0 ; cnt < MAX_CLASS ; cnt++) { if (!class_level(ch, cnt)) continue; if (class_table[cnt].attr_prime == stat) bonus += class_level(ch, cnt) / 2; else if (class_table[cnt].attr_second == stat) bonus += class_level(ch, cnt) / 3; } pop_call(); return bonus; } int str_roll( CHAR_DATA *ch ) { int roll; push_call("str_roll(%p)",ch); roll = dice(1,20); roll += stat_mods(ch, APPLY_STR); if (has_armor_penalty(ch)) roll -= armor_check_penalty(ch); roll += class_stat_bonus(ch, APPLY_STR); pop_call(); return( roll ); } int dex_roll( CHAR_DATA *ch ) { int roll; push_call("dex_roll(%p)",ch); roll = dice(1,20); roll += stat_mods(ch, APPLY_DEX); if (has_armor_penalty(ch)) roll -= armor_check_penalty(ch); roll += class_stat_bonus(ch, APPLY_DEX); pop_call(); return( roll ); } int con_roll( CHAR_DATA *ch ) { int roll; push_call("con_roll(%p)",ch); roll = dice(1,20); roll += stat_mods(ch, APPLY_CON); roll += class_stat_bonus(ch, APPLY_CON); pop_call(); return( roll ); } int int_roll( CHAR_DATA *ch ) { int roll; push_call("int_roll(%p)",ch); roll = dice(1,20); roll += stat_mods(ch, APPLY_INT); roll += class_stat_bonus(ch, APPLY_INT); pop_call(); return( roll ); } int wis_roll( CHAR_DATA *ch ) { int roll; push_call("wis_roll(%p)",ch); roll = dice(1,20); roll += stat_mods(ch, APPLY_WIS); roll += class_stat_bonus(ch, APPLY_WIS); pop_call(); return( roll ); } int cha_roll( CHAR_DATA *ch ) { int roll; push_call("cha_roll(%p)",ch); roll = dice(1,20); roll += stat_mods(ch, APPLY_CHA); roll += class_stat_bonus(ch, APPLY_CHA); pop_call(); return( roll ); } /* * Retrieve a character's carry capacity. * (a mud thing, not SRD, but added SRD goodness) - Kregor * To discourage lugging too much inventory without a container or three */ int can_carry_n( CHAR_DATA *ch ) { int inv; push_call("can_carry_n(%p)",ch); inv = MAX_WEAR + ch->perm_dex + ch->level; if (learned(ch, gsn_packrat)) inv += 2; if (IS_IMMORTAL(ch)) { pop_call(); return 1000; } switch (get_size(ch)) { case SIZE_FINE: inv = inv / 3; break; case SIZE_DIMINUTIVE: inv = inv / 2; break; case SIZE_TINY: inv = inv * 2 / 3; break; case SIZE_SMALL: inv = inv * 3 / 4; break; case SIZE_LARGE: inv *= 2; break; case SIZE_HUGE: inv *= 4; break; case SIZE_GARGANTUAN: inv *= 6; break; case SIZE_COLOSSAL: inv *= 8; break; } if (quadruped(ch)) inv *= 2; if (many_legged(ch)) inv *= 4; if (is_affected(ch, gsn_floating_disc)) inv *= 2; if (IS_NPC(ch)) { if (IS_SET(ch->act, ACT_WEAK)) { pop_call(); return 1; } } pop_call(); return inv; } /* * Carry weight tables under d20 SRD - Kregor */ const int max_carry_load[] = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 115, 130, 150, 175, 200, 230, 260, 300, 350, 400, 460, 520, 600, 700, 800, 920, 1040, 1200, 1400, 1600, }; int get_carry_w (CHAR_DATA *ch) { OBJ_DATA *obj; int weight; push_call("get_carry_w(%p)",ch); for (weight = 0, obj = ch->first_carrying ; obj ; obj = obj->next_content) { if (obj->weight) weight += get_obj_weight(obj); } if (!IS_NPC(ch)) weight += ch->gold / 500; pop_call(); return weight; } /* * Retrieve a character's carry capacity. * Revised for SRD, and tenths of a pound - Kregor */ int can_carry_w( CHAR_DATA *ch ) { int stat, total, pounds; push_call("can_carry_w(%p)",ch); //incorporal beings can only carry incorporeal items, which also weigh 0 if (IS_NPC(ch) && rspec_req(ch, RSPEC_INCORPOREAL)) { pop_call(); return 0; } stat = UMAX(0, UMIN(99, get_curr_str(ch))); if (learned(ch, gsn_packrat)) stat += 2; total = 1; while (stat > 30) { stat -= 10; total *= 4; } pounds = total * max_carry_load[stat]; //to support 1/10ths of a pound pounds *= 10; if (many_legged(ch) || quadruped(ch)) { switch (get_size(ch)) { case SIZE_FINE: pounds = pounds / 4; break; case SIZE_DIMINUTIVE: pounds = pounds / 2; break; case SIZE_TINY: pounds = pounds * 3 / 4; break; case SIZE_MEDIUM: pounds = pounds * 3 / 2; break; case SIZE_LARGE: pounds *= 3; break; case SIZE_HUGE: pounds *= 6; break; case SIZE_GARGANTUAN: pounds *= 12; break; case SIZE_COLOSSAL: pounds *= 24; break; } } else { switch (get_size(ch)) { case SIZE_FINE: pounds = pounds / 8; break; case SIZE_DIMINUTIVE: pounds = pounds / 4; break; case SIZE_TINY: pounds = pounds / 2; break; case SIZE_SMALL: pounds = pounds * 3 / 4; break; case SIZE_LARGE: pounds *= 2; break; case SIZE_HUGE: pounds *= 4; break; case SIZE_GARGANTUAN: pounds *= 8; break; case SIZE_COLOSSAL: pounds *= 16; break; } } if (many_legged(ch)) pounds *= 8; else if (quadruped(ch)) pounds *= 4; // add allowance for floating disc spell if (is_affected(ch, gsn_floating_disc)) pounds += 1000 * get_affect_level(ch, gsn_floating_disc); if (IS_ACT(ch, ACT_WEAK)) pounds = 10; pop_call(); return pounds; } /* * Height weight tables. Sadly only calculates * the standard player races. Should probably * re-do race tables for all races and call the * table with this function instead. - Kregor */ void height_weight(CHAR_DATA * ch) { int mod; push_call("height_weight(%p)",ch); ch->height = number_fuzzy(race_table[ch->race].height); ch->weight = number_fuzzy(race_table[ch->race].weight); if (ch->race == RACE_HUMAN && ch->sex == SEX_MALE) { mod = dice(2,10); ch->height = 58 + mod; ch->weight = 120 + (dice(2,4)*mod); } if (ch->race == RACE_HUMAN && ch->sex == SEX_FEMALE) { mod = dice(2,10); ch->height = 53 + mod; ch->weight = 85 + (dice(2,4)*mod); } if (IS_RACE(ch, RACE_DWARF) && ch->sex == SEX_MALE) { mod = dice(2,4); ch->height = 45 + mod; ch->weight = 130 + (dice(2,6)*mod); } if (IS_RACE(ch, RACE_DWARF) && ch->sex == SEX_FEMALE) { mod = dice(2,4); ch->height = 43 + mod; ch->weight = 100 + (dice(2,6)*mod); } if (IS_RACE(ch, RACE_ELF) && ch->sex == SEX_MALE) { mod = dice(2,6); ch->height = 53 + mod; ch->weight = 85 + (dice(1,6)*mod); } if (IS_RACE(ch, RACE_ELF) && ch->sex == SEX_FEMALE) { mod = dice(2,6); ch->height = 53 + mod; ch->weight = 80 + (dice(1,6)*mod); } if (IS_RACE(ch, RACE_GNOME) && ch->sex == SEX_MALE) { mod = dice(2,4); ch->height = 36 + mod; ch->weight = 40 + mod; } if (IS_RACE(ch, RACE_GNOME) && ch->sex == SEX_FEMALE) { mod = dice(2,4); ch->height = 34 + mod; ch->weight = 35 + mod; } if (IS_RACE(ch, RACE_HALFELF) && ch->sex == SEX_MALE) { mod = dice(2,8); ch->height = 55 + mod; ch->weight = 40 + (dice(2,4)*mod); } if (IS_RACE(ch, RACE_HALFELF) && ch->sex == SEX_FEMALE) { mod = dice(2,8); ch->height = 53 + mod; ch->weight = 35 + (dice(2,4)*mod); } if (IS_RACE(ch, RACE_HALFORC) && ch->sex == SEX_MALE) { mod = dice(2,12); ch->height = 58 + mod; ch->weight = 150 + (dice(2,6)*mod); } if (IS_RACE(ch, RACE_HALFORC) && ch->sex == SEX_FEMALE) { mod = dice(2,12); ch->height = 53 + mod; ch->weight = 110 + (dice(2,6)*mod); } if (IS_RACE(ch, RACE_HALFLING) && ch->sex == SEX_MALE) { mod = dice(2,4); ch->height = 32 + mod; ch->weight = 30 + mod; } if (IS_RACE(ch, RACE_HALFLING) && ch->sex == SEX_FEMALE) { mod = dice(2,4); ch->height = 30 + mod; ch->weight = 25 + mod; } if (ch->race == RACE_DROW && ch->sex == SEX_MALE) { mod = dice(2,4); ch->height = 53 + mod; ch->weight = 85 + (dice(1,4)*mod); } if (ch->race == RACE_DROW && ch->sex == SEX_FEMALE) { mod = dice(2,4); ch->height = 53 + mod; ch->weight = 80 + (dice(1,4)*mod); } if (ch->race == RACE_ORC && ch->sex == SEX_MALE) { mod = dice(2,12); ch->height = 60 + mod; ch->weight = 160 + (dice(2,8)*mod); } if (ch->race == RACE_ORC && ch->sex == SEX_FEMALE) { mod = dice(2,12); ch->height = 56 + mod; ch->weight = 130 + (dice(2,8)*mod); } pop_call(); return; } /* factor applies and affects into height wieght and size - Kregor */ int get_height( CHAR_DATA *ch ) { int height; push_call("get_height(%p)",ch); height = ch->height; if (get_apply(ch, APPLY_SIZE) > 0) height *= get_apply(ch, APPLY_SIZE) * 2; else if (get_apply(ch, APPLY_SIZE) < 0) height /= abs(get_apply(ch, APPLY_SIZE)) * 2; pop_call(); return height; } int get_weight( CHAR_DATA *ch ) { int weight; push_call("get_weight(%p)",ch); weight = ch->weight; if (get_apply(ch, APPLY_SIZE) > 0) weight *= get_apply(ch, APPLY_SIZE) * 2; else if (get_apply(ch, APPLY_SIZE) < 0) weight /= abs(get_apply(ch, APPLY_SIZE)) * 2; pop_call(); return weight; } int get_size(CHAR_DATA * ch) { int size; push_call("get_size(%p)",ch); size = race_table[get_race(ch)].size; size += get_apply(ch, APPLY_SIZE); pop_call(); return (URANGE(SIZE_FINE, size, SIZE_COLOSSAL)); } /* calc the maximum hit points for ch */ int get_max_hit( CHAR_DATA *ch ) { int pts; push_call("get_max_hit(%p)",ch); pts = ch->max_hit + (stat_bonus(TRUE, ch, APPLY_CON) * ch->level); pts += get_apply(ch, APPLY_HIT); /* reworked toughness feat */ if (learned(ch, gsn_toughness)) pts += ch->level; if (learned(ch, gsn_secret_of_health)) pts += ch->level; /* this causes a loop thru PET_DATA each * time it's called, hopefully not too much */ if (has_familiar(ch, RACE_TOAD)) pts += ch->level; /* -5 hp for every negative level per SRD */ pts += get_apply(ch, APPLY_LEVEL) * 5; pts = UMAX(ch->level, pts); pop_call(); return(pts); } /* calc the maximum move points for ch */ int get_max_move( CHAR_DATA *ch ) { int pts; push_call("get_max_move(%p)",ch); pts = stat_bonus(TRUE, ch, APPLY_CON) * ch->level + ch->max_move; pts += get_apply(ch, APPLY_MOVE); if (many_legged(ch) || quadruped(ch)) pts *= 4; pts = UMAX(ch->level, pts); pop_call(); return(pts); } /* no move = exhaustion */ bool IS_EXHAUSTED( CHAR_DATA *ch ) { push_call("IS_EXHAUSTED(%p)",ch); if (IS_AFFECTED(ch, AFF2_EXHAUSTED)) { pop_call(); return TRUE; } if (ch->move <= 0) { pop_call(); return TRUE; } pop_call(); return FALSE; } /* 1/3 of move = fatigued */ bool IS_FATIGUED( CHAR_DATA *ch ) { push_call("IS_FATIGUED(%p)",ch); /* If you're already worse, you're not this */ if (IS_EXHAUSTED(ch)) { pop_call(); return FALSE; } if (IS_AFFECTED(ch, AFF2_FATIGUED)) { pop_call(); return TRUE; } if (ch->move < get_max_move(ch) / 3) { pop_call(); return TRUE; } /* hungry and dehydrated = fatigue until sated */ if (!IS_NPC(ch)) { if (ch->pcdata->condition[COND_THIRST] <= 0 || ch->pcdata->condition[COND_FULL] <= 0) { pop_call(); return TRUE; } } pop_call(); return FALSE; } /* check for staggered condition */ bool IS_STAGGERED( CHAR_DATA *ch ) { push_call("IS_STAGGERED(%p)",ch); /* If you're out, you're not moving */ if (!IS_AWAKE(ch)) { pop_call(); return FALSE; } if (ch->nonlethal == ch->hit || ch->hit <= 0) { pop_call(); return TRUE; } if (ch->move <= 0) { pop_call(); return TRUE; } if (!IS_AFFECTED(ch, AFF_FREEDOM)) { if (IS_AFFECTED(ch, AFF2_STAGGERED) || IS_UNDERWATER(ch)) { pop_call(); return TRUE; } } pop_call(); return FALSE; } int level_diff(CHAR_DATA *ch) { int lev, cnt; int count = 0; int level1 = 0; int level2 = 0; push_call("level_diff(%p)",ch); if (IS_NPC(ch)) { pop_call(); return 0; } for (lev = 1 ; lev <= LEVEL_HERO ; lev++) { for (cnt = 0 ; cnt < CLASS_ARCANE_ARCHER ; cnt++) { if (favored_class(ch) == cnt) continue; if (lev == class_level(ch, cnt)) level2 = lev; } if (level1 > 0 && level1+2 <= level2) count += 1; level1 = level2; } pop_call(); return count; } int favored_class(CHAR_DATA *ch) { int cnt = 0; int class = 0; push_call("favored_class(%p)",ch); class = CLASS_MONSTER; switch (ch->race) { case RACE_HUMAN: case RACE_HALFELF: for (cnt = 0 ; cnt < MAX_CLASS ; cnt++) { if (!ch->mclass[cnt]) { continue; } if (ch->mclass[cnt] > class) { class = cnt; } } break; case RACE_DROW: if (ch->sex == SEX_MALE) { class = CLASS_WIZARD; break; } else { class = CLASS_FIGHTER; break; } break; case RACE_GNOME: /* bring back gnome illusionists! */ if (get_school(ch) == SCHOOL_ILLUSION) { class = CLASS_WIZARD; break; } else { class = CLASS_BARD; break; } break; default: class = race_table[ch->race].favored_class; break; } pop_call(); return class; } /* calcs starting gold at character gen - Kregor */ int starting_gold ( CHAR_DATA * ch ) { int gold = 0; push_call("starting_gold(%p)",ch); switch (ch->class) { default: bug("starting_gold: unknown starting class.", 0); break; case CLASS_BARBARIAN: gold = dice(3, 6) * 1000; break; case CLASS_BARD: gold = dice(3, 6) * 1000; break; case CLASS_CLERIC: gold = dice(4, 6) * 1000; break; case CLASS_DRUID: gold = dice(2, 6) * 1000; break; case CLASS_FIGHTER: gold = dice(5, 6) * 1000; break; case CLASS_MONK: gold = dice(1, 6) * 1000; break; case CLASS_PALADIN: gold = dice(5, 6) * 1000; break; case CLASS_RANGER: gold = dice(4, 6) * 1000; break; case CLASS_ROGUE: case CLASS_EXPERT: gold = dice(4, 6) * 1000; break; case CLASS_SORCERER: gold = dice(2, 6) * 1000; break; case CLASS_WIZARD: gold = dice(2, 6) * 1000; break; } if (learned(ch, gsn_noble_birth)) gold += 10000; pop_call(); return gold; } /* Max new languages a CHAR can learn - Kregor */ int max_tongues(CHAR_DATA *ch) { int max; push_call("max_tongues(%p)",ch); if (IS_NPC(ch)) { pop_call(); return 0; } max = stat_bonus(FALSE, ch, APPLY_INT); if (learned(ch, gsn_linguist)) max += 2; max += class_level(ch, CLASS_LOREMASTER) / 4; pop_call(); return max; } /* * tally the current starting tongues for char besides * automatic ones - Kregor */ int count_tongues(CHAR_DATA *ch) { int cnt, count; push_call("max_tongues(%p)",ch); if (IS_NPC(ch)) { pop_call(); return 0; } for (cnt = count = 0 ; cnt < MAX_LANG - 2 ; cnt++) { if (race_table[ch->race].speaks == 1 << cnt) continue; if (IS_SET(race_table[ch->race].understands, 1 << cnt)) continue; if (IS_SET(ch->language, 1 << cnt)) count++; } pop_call(); return count; } /* tally number of bard songs learnable - Kregor */ int max_songs(CHAR_DATA *ch) { int level; push_call("max_songs(%p)",ch); if ((level = multi_class_level(ch, gsn_bardic_song)) <= 0) { pop_call(); return 0; } pop_call(); return (level / 3); } int count_songs(CHAR_DATA *ch) { int sn, count; push_call("count_songs(%p)",ch); if (IS_NPC(ch)) { pop_call(); return 0; } for (sn = count = 0 ; *skill_table[sn].name != '\0' ; sn++) { if (skill_table[sn].skilltype != FSKILL_BARDSONG) continue; if (learned(ch, sn)) count++; } pop_call(); return count; } /* * Compute saving throws. * d20 variant rule: added medium save progression. * d20 variant rule: Instead of computing saves per class, for * multiclass characters, total the levels of all classes in * each given save progression, and base the total bonus on * combined levels of each - Kregor */ int base_fort_save( CHAR_DATA *ch ) { float save; push_call("base_fort_save(%p)",ch); if (!IS_CLASSED(ch)) { switch (race_type_table[race_type(ch)].fort_save) { case SAVE_HIGH: save = ch->level / 2 + 2; break; case SAVE_MED: save = ch->level / 2.5 + 1; break; default: save = ch->level / 3; break; } pop_call(); return (save); } // if (ch->level == 1) // { // switch (class_table[ch->class].fort_save) // { // case SAVE_HIGH: // save = class_level(ch, ch->class) / 2 + 2; // break; // case SAVE_MED: // save = class_level(ch, ch->class) / 2.5 + 1; // break; // default: // save = class_level(ch, ch->class) / 3; // break; // } // pop_call(); // return (save); // } // int save_hi, save_md, save_lo, cnt; for (cnt = save_hi = save_md = save_lo = 0 ; cnt < MAX_CLASS ; cnt++) { if (class_level(ch, cnt) <= 0) continue; if (cnt == CLASS_MONSTER) { if (race_type_table[race_type(ch)].fort_save == SAVE_HIGH) save_hi += class_level(ch, cnt); else if (race_type_table[race_type(ch)].fort_save == SAVE_MED) save_md += class_level(ch, cnt); else save_lo += class_level(ch, cnt); continue; } else { if (class_table[cnt].fort_save == SAVE_HIGH) save_hi += class_level(ch, cnt); else if (class_table[cnt].fort_save == SAVE_MED) save_md += class_level(ch, cnt); else save_lo += class_level(ch, cnt); } } save = 0; if (save_hi > 0) save += save_hi / 2 + 2; if (save_md > 0) save += save_md / 2.5 + 1; if (save_lo > 0) save += save_lo / 3; pop_call(); return round(save); } int get_fort_save( CHAR_DATA *ch ) { int save; push_call("get_fort_save(%p)",ch); save = base_fort_save(ch); if ((is_affected(ch, gsn_bestow_grace) || learned(ch, gsn_divine_grace)) && stat_bonus(TRUE, ch, APPLY_CHA) > 0) { save += UMIN(stat_bonus(TRUE, ch, APPLY_CHA), multi_class_level(ch, gsn_divine_grace)); } save += stat_bonus(TRUE, ch, APPLY_CON); save += get_apply(ch, APPLY_SAVING_FORT); save += combine_apply(ch, APPLY_COMP_FORT, APPLY_COMP_SAVES); save += combine_apply(ch, APPLY_INS_FORT, APPLY_INS_SAVES); save += combine_apply(ch, APPLY_LUCK_FORT, APPLY_LUCK_SAVES); save += combine_apply(ch, APPLY_MOR_FORT, APPLY_MOR_SAVES); save += get_apply(ch, APPLY_LEVEL); if (IS_AFFECTED(ch, AFF2_SICKENED)) save -= 2; if (ch->fear_level > 0) save -= 2; if (has_familiar(ch, RACE_RODENT)) save += 2; if (learned(ch, gsn_secret_of_stamina)) save += 2; pop_call(); return(save); } int base_refl_save( CHAR_DATA *ch ) { float save; push_call("base_refl_save(%p)",ch); if (!IS_CLASSED(ch)) { switch (race_type_table[race_type(ch)].refl_save) { case SAVE_HIGH: save = ch->level / 2 + 2; break; case SAVE_MED: save = ch->level / 2.5 + 1; break; default: save = ch->level / 3; break; } pop_call(); return (save); } // if (ch->level == 1) // { // switch (class_table[ch->class].refl_save) // { // case SAVE_HIGH: // save = class_level(ch, ch->class) / 2 + 2; // break; // case SAVE_MED: // save = class_level(ch, ch->class) / 2.5 + 1; // break; // default: // save = class_level(ch, ch->class) / 3; // break; // } // pop_call(); // return (save); // } // int save_hi, save_md, save_lo, cnt; for (cnt = save_hi = save_md = save_lo = 0 ; cnt < MAX_CLASS ; cnt++) { if (class_level(ch, cnt) <= 0) continue; if (cnt == CLASS_MONSTER) { if (race_type_table[race_type(ch)].refl_save == SAVE_HIGH) save_hi += class_level(ch, cnt); else if (race_type_table[race_type(ch)].refl_save == SAVE_MED) save_md += class_level(ch, cnt); else save_lo += class_level(ch, cnt); continue; } else { if (class_table[cnt].refl_save == SAVE_HIGH) save_hi += class_level(ch, cnt); else if (class_table[cnt].refl_save == SAVE_MED) save_md += class_level(ch, cnt); else save_lo += class_level(ch, cnt); } } save = 0; if (save_hi > 0) save += save_hi / 2 + 2; if (save_md > 0) save += save_md / 2.5 + 1; if (save_lo > 0) save += save_lo / 3; pop_call(); return round(save); } int get_refl_save( CHAR_DATA *ch ) { int save, plus; OBJ_DATA *shield; push_call("get_refl_save(%p)",ch); save = base_refl_save(ch); switch(drunk_level(ch)) { case DRUNK_SOBER: break; case DRUNK_TIPSY: save -= 1; break; case DRUNK_MERRY: save -= 2; break; case DRUNK_DRUNK: save -= 4; break; case DRUNK_HAMMERED: save -= 8; break; default: save -= 16; break; } if ((is_affected(ch, gsn_bestow_grace) || learned(ch, gsn_divine_grace)) && stat_bonus(TRUE, ch, APPLY_CHA) > 0) { save += UMIN(stat_bonus(TRUE, ch, APPLY_CHA), multi_class_level(ch, gsn_divine_grace)); } if ((plus = multi_class_level(ch, gsn_grace)) >= 4) { shield = get_eq_char(ch, WEAR_SHIELD); if (armor_type_worn(ch) == ARMOR_NONE && !shield) { save += (plus / 4) * 2; } } save += stat_bonus(TRUE, ch, APPLY_DEX); if (get_monk_style(ch) == STYLE_INVISIBLE_EYE && class_level(ch, CLASS_MONK) >= 12) save += 2; save += get_apply(ch, APPLY_SAVING_REFL); save += combine_apply(ch, APPLY_COMP_REFL, APPLY_COMP_SAVES); save += combine_apply(ch, APPLY_INS_REFL, APPLY_INS_SAVES); save += combine_apply(ch, APPLY_LUCK_REFL, APPLY_LUCK_SAVES); save += combine_apply(ch, APPLY_MOR_REFL, APPLY_MOR_SAVES); save += get_apply(ch, APPLY_LEVEL); // armor penalty applies to non-proficient armor if (has_armor_penalty(ch)) save -= armor_check_penalty(ch); if (IS_AFFECTED(ch, AFF2_SICKENED)) save -= 2; if (ch->fear_level > 0) save -= 2; if (IS_AFFECTED(ch, AFF2_FASCINATED)) save -= 4; if (has_familiar(ch, RACE_WEASEL)) save += 2; if (learned(ch, gsn_secret_of_avoidance)) save += 2; pop_call(); return(save); } int base_will_save( CHAR_DATA *ch ) { float save; push_call("base_will_save(%p)",ch); if (!IS_CLASSED(ch)) { switch (race_type_table[race_type(ch)].will_save) { case SAVE_HIGH: save = ch->level / 2 + 2; break; case SAVE_MED: save = ch->level / 2.5 + 1; break; default: save = ch->level / 3; break; } pop_call(); return (save); } // if (ch->level == 1) // { // switch (class_table[ch->class].will_save) // { // case SAVE_HIGH: // save = class_level(ch, ch->class) / 2 + 2; // break; // case SAVE_MED: // save = class_level(ch, ch->class) / 2.5 + 1; // break; // default: // save = class_level(ch, ch->class) / 3; // break; // } // pop_call(); // return (save); // } // int save_hi, save_md, save_lo, cnt; for (cnt = save_hi = save_md = save_lo = 0 ; cnt < MAX_CLASS ; cnt++) { if (class_level(ch, cnt) <= 0) continue; if (cnt == CLASS_MONSTER) { if (race_type_table[race_type(ch)].will_save == SAVE_HIGH) save_hi += class_level(ch, cnt); else if (race_type_table[race_type(ch)].will_save == SAVE_MED) save_md += class_level(ch, cnt); else save_lo += class_level(ch, cnt); continue; } else { if (class_table[cnt].will_save == SAVE_HIGH) save_hi += class_level(ch, cnt); else if (class_table[cnt].will_save == SAVE_MED) save_md += class_level(ch, cnt); else save_lo += class_level(ch, cnt); } } save = 0; if (save_hi > 0) save += save_hi / 2 + 2; if (save_md > 0) save += save_md / 2.5 + 1; if (save_lo > 0) save += save_lo / 3; pop_call(); return round(save); } int get_will_save( CHAR_DATA *ch ) { int save; push_call("get_will_save(%p)",ch); save = base_will_save(ch); if ((is_affected(ch, gsn_bestow_grace) || learned(ch, gsn_divine_grace)) && stat_bonus(TRUE, ch, APPLY_CHA) > 0) { save += UMIN(stat_bonus(TRUE, ch, APPLY_CHA), multi_class_level(ch, gsn_divine_grace)); } save += stat_bonus(TRUE, ch, APPLY_WIS); save += get_apply(ch, APPLY_SAVING_WILL); save += combine_apply(ch, APPLY_COMP_WILL, APPLY_COMP_SAVES); save += combine_apply(ch, APPLY_INS_WILL, APPLY_INS_SAVES); save += combine_apply(ch, APPLY_LUCK_WILL, APPLY_LUCK_SAVES); save += combine_apply(ch, APPLY_MOR_WILL, APPLY_MOR_SAVES); save += get_apply(ch, APPLY_LEVEL); if (learned(ch, gsn_secret_of_resolve)) save += 2; if (IS_AFFECTED(ch, AFF2_SICKENED)) save -= 2; if (ch->fear_level > 0) save -= 2; pop_call(); return(save); } /* * Return the flag for sorcerer bloodline - Kregor */ int get_bloodline( CHAR_DATA *ch ) { int num = -1; push_call("get_bloodline(%p)",ch); if (!class_level(ch, CLASS_SORCERER)) { pop_call(); return -1; } if (IS_NPC(ch)) num = BLOODLINE_DRACONIC; else if (ch->learned[gsn_aberrant_bloodline]) num = BLOODLINE_ABERRANT; else if (ch->learned[gsn_abyssal_bloodline]) num = BLOODLINE_ABYSSAL; else if (ch->learned[gsn_arcane_bloodline]) num = BLOODLINE_ARCANE; else if (ch->learned[gsn_celestial_bloodline]) num = BLOODLINE_CELESTIAL; else if (ch->learned[gsn_destined_bloodline]) num = BLOODLINE_DESTINED; else if (ch->learned[gsn_air_bloodline]) num = BLOODLINE_AIR; else if (ch->learned[gsn_fire_bloodline]) num = BLOODLINE_FIRE; else if (ch->learned[gsn_earth_bloodline]) num = BLOODLINE_EARTH; else if (ch->learned[gsn_water_bloodline]) num = BLOODLINE_WATER; else if (ch->learned[gsn_fey_bloodline]) num = BLOODLINE_FEY; else if (ch->learned[gsn_infernal_bloodline]) num = BLOODLINE_INFERNAL; else if (ch->learned[gsn_undead_bloodline]) num = BLOODLINE_UNDEAD; pop_call(); return num; } /* * Return the flag for combat style * NPCs without a style set default to d20 originals - Kregor */ int get_monk_style( CHAR_DATA *ch ) { int num = 0; push_call("get_monk_style(%p)",ch); if (!class_level(ch, CLASS_MONK)) { pop_call(); return 0; } if (ch->learned[gsn_style_cobra_strike]) num = STYLE_COBRA_STRIKE; else if (ch->learned[gsn_style_denying_stance]) num = STYLE_DENYING_STANCE; else if (ch->learned[gsn_style_hand_and_foot]) num = STYLE_HAND_AND_FOOT; else if (ch->learned[gsn_style_invisible_eye]) num = STYLE_INVISIBLE_EYE; else if (ch->learned[gsn_style_passive_way]) num = STYLE_PASSIVE_WAY; else if (ch->learned[gsn_style_sleeping_tiger]) num = STYLE_SLEEPING_TIGER; else if (ch->learned[gsn_style_undying_way]) num = STYLE_UNDYING_WAY; else if (IS_NPC(ch)) num = STYLE_HAND_AND_FOOT; pop_call(); return num; } int get_ranger_style( CHAR_DATA *ch ) { int num = 0; push_call("get_ranger_style(%p)",ch); if (!class_level(ch, CLASS_RANGER)) { pop_call(); return 0; } if (ch->learned[gsn_style_melee]) num = STYLE_MELEE; else if (ch->learned[gsn_style_archery]) num = STYLE_ARCHERY; else if (ch->learned[gsn_style_horseback]) num = STYLE_HORSEBACK; else if (ch->learned[gsn_style_skirmish]) num = STYLE_SKIRMISHING; else if (ch->learned[gsn_style_strongarm]) num = STYLE_STRONGARM; else if (ch->learned[gsn_style_throwing]) num = STYLE_THROWING; else if (IS_NPC(ch)) num = STYLE_MELEE; pop_call(); return num; } /* * Return the flag for wizard school - Kregor */ int get_school( CHAR_DATA *ch ) { int num = -1; push_call("get_school(%p)",ch); if (!class_level(ch, CLASS_WIZARD)) { pop_call(); return -1; } if (ch->learned[gsn_school_univ]) num = SCHOOL_UNIVERSAL; else if (ch->learned[gsn_school_abj]) num = SCHOOL_ABJURATION; else if (ch->learned[gsn_school_conj]) num = SCHOOL_CONJURATION; else if (ch->learned[gsn_school_div]) num = SCHOOL_DIVINATION; else if (ch->learned[gsn_school_ench]) num = SCHOOL_ENCHANTMENT; else if (ch->learned[gsn_school_evoc]) num = SCHOOL_EVOCATION; else if (ch->learned[gsn_school_illus]) num = SCHOOL_ILLUSION; else if (ch->learned[gsn_school_necro]) num = SCHOOL_NECROMANCY; else if (ch->learned[gsn_school_trans]) num = SCHOOL_TRANSMUTATION; pop_call(); return num; } /* * Return clerical domains - Kregor */ bool has_domain( CHAR_DATA *ch, int dom ) { push_call("has_domain(%p)",ch); if (!class_level(ch, CLASS_CLERIC)) { pop_call(); return FALSE; } if (IS_NPC(ch)) { if (ch->god && (god_table[ch->god].domain1 == dom || (class_level(ch, CLASS_CLERIC) > 10 && god_table[ch->god].domain2 == dom))) { pop_call(); return TRUE; } pop_call(); return FALSE; } pop_call(); return ch->pcdata->domain[dom]; } /* * is cleric apotheosized? - Kregor */ bool domain_apotheosis( CHAR_DATA *ch, int dom ) { push_call("domain_apotheosis(%p)",ch); if (class_level(ch, CLASS_CLERIC) < 20) { pop_call(); return FALSE; } pop_call(); return has_domain(ch, dom); } /* * check sorcerer capstone. - Kregor */ bool bloodline_capstone( CHAR_DATA *ch, int bloodline ) { push_call("bloodline_capstone(%p)",ch); if (class_level(ch, CLASS_SORCERER) < 20) { pop_call(); return FALSE; } pop_call(); return (get_bloodline(ch) == bloodline); } /* * check arcane mastery. - Kregor */ bool arcane_mastery( CHAR_DATA *ch, int school ) { push_call("arcane_mastery(%p)",ch); if (class_level(ch, CLASS_WIZARD) < 20) { pop_call(); return FALSE; } pop_call(); return (get_school(ch) == school); } /* * Extensively reworked to return ranks in skills, * Activate race and class abilities at a given level, * and return classes a spell is known in - Kregor */ int learned( CHAR_DATA *ch, int sn ) { int cnt, rank; push_call("learned(%p,%p)",ch,sn); if (ch == NULL) { pop_call(); return 0; } if (!is_string(skill_table[sn].name)) { pop_call(); return 0; } /* * NPC skill ranks: check for hard-set first, then * assign ranks based on class, fall back to racial skills. */ if (IS_NPC(ch) && skill_table[sn].skilltype == FSKILL_SKILL) { if ((rank = ch->learned[sn]) <= 0) { if (IS_CLASSED(ch)) { if ((cnt = multi(ch, sn)) != -1) { rank = class_level(ch, cnt); } else { rank = 0; } } if (get_race(ch) > RACE_NONE && race_skill(ch, sn) > rank) { rank = race_skill(ch, sn); } } pop_call(); return UMAX(0, number_fuzzy(rank)); } if (skill_table[sn].skilltype == FSKILL_SPELL) { int class = ch->learned[sn]; /* * NPC spells are known and cast at highest casting class */ if (IS_NPC(ch)) { if ((class = multi(ch, sn)) != -1) { pop_call(); return (1 << class); } pop_call(); return 0; } if (opposing_domain(ch, sn)) { pop_call(); return 0; } if (domain_skill(ch, sn)) { SET_SHIFT(class, CLASS_CLERIC); } if (bloodline_skill(ch, sn)) { SET_SHIFT(class, CLASS_SORCERER); } for (cnt = 0 ; cnt < MAX_CLASS ; cnt++) { if (class_level(ch, cnt) <= 0) continue; if (IS_GOD(ch)) { SET_SHIFT(class, cnt); } if (cnt == CLASS_WIZARD && opposing_school(ch, sn)) // wizards can't have opposing schools continue; if (ch->learned[gsn_shadow_casting] && IS_SET(skill_table[sn].spell_desc, SDESC_LIGHT)) continue; if (cnt == CLASS_WIZARD && skill_table[sn].skill_level[cnt] == 0) // automatic cantrips SET_SHIFT(class, cnt); if (cnt == CLASS_BLACKGUARD && !IS_EVIL(ch)) // non evil blackguards lose spells continue; if (cnt == CLASS_PALADIN && (!IS_GOOD(ch) || !IS_LAWFUL(ch))) // non LG pallys lose spells continue; if (cnt == CLASS_PALADIN && (!IS_NPC(ch) && ch->pcdata->reputation < 9)) // Pally reputation matters! continue; if (class_table[cnt].attr_prime == APPLY_WIS) { if (max_spell_circle(ch, cnt) >= skill_table[sn].skill_level[cnt]) { SET_SHIFT(class, cnt); } } else if (skill_table[sn].skill_level[cnt] == 0) { SET_SHIFT(class, cnt); } } pop_call(); return class; } if (skill_table[sn].skilltype == FSKILL_ABILITY) { if (class_ability(ch, sn) || race_skill(ch, sn)) { pop_call(); return (UMAX(1,ch->learned[sn])); } } if (skill_table[sn].skilltype == FSKILL_RACEATTACK && race_skill(ch, sn)) { pop_call(); return (UMAX(1,ch->learned[sn])); } if (skill_table[sn].skilltype == FSKILL_BARDSONG) { if (multi_class_level(ch, gsn_bardic_song) >= skill_table[sn].skill_level[CLASS_BARD]) { pop_call(); return (UMAX(1,ch->learned[sn])); } } if (skill_table[sn].skilltype == FSKILL_FEAT) { // give imp. unarmed strike to clerics of deities w/ unarmed strike as fave weapon - Kregor if (sn == gsn_imp_unarmed_strike) { if (class_level(ch, CLASS_CLERIC) && ch->god && god_table[ch->god].favored_weapon == WEAPON_TYPE_WEAPON) { pop_call(); return (UMAX(1,ch->learned[sn])); } } if (class_ability(ch, sn) || race_skill(ch, sn)) { pop_call(); return (UMAX(1,ch->learned[sn])); } } /* Weapon proficiencies by class */ if (skill_table[sn].skilltype == FSKILL_WEAPON) { int skill = ch->learned[sn]; if (skill_table[sn].spell_desc == WEAPON_CLASS_SIMPLE && sn != gsn_unarmed_strike && (ch->learned[gsn_weapon_prof_simple] || class_ability(ch, gsn_weapon_prof_simple))) { SET_BIT(skill, WSKILL_PROFICIENT); } if (skill_table[sn].spell_desc == WEAPON_CLASS_MARTIAL && (ch->learned[gsn_weapon_prof_martial] || class_ability(ch, gsn_weapon_prof_martial))) { SET_BIT(skill, WSKILL_PROFICIENT); } if (skill_table[sn].spell_desc == WEAPON_CLASS_EXOTIC && (ch->learned[gsn_weapon_prof_exotic] || class_ability(ch, gsn_weapon_prof_exotic))) { SET_BIT(skill, WSKILL_PROFICIENT); } if (class_ability(ch, sn) || race_skill(ch, sn)) { SET_BIT(skill, WSKILL_PROFICIENT); } if (sn == gsn_unarmed_strike) { if (class_level(ch, CLASS_CLERIC) && ch->god && god_table[ch->god].favored_weapon == WEAPON_TYPE_WEAPON) { SET_BIT(skill, WSKILL_PROFICIENT); if (has_domain(ch, DOMAIN_WAR)) { SET_BIT(skill, WSKILL_SPECIALIZED); } } else if (class_ability(ch, gsn_imp_unarmed_strike) || ch->learned[gsn_imp_unarmed_strike] || class_ability(ch, gsn_martial_arts) || ch->learned[gsn_martial_arts]) { SET_BIT(skill, WSKILL_PROFICIENT); } } pop_call(); return skill; } if ((sn == gsn_evasion || sn == gsn_imp_evasion) && IS_ACT(ch, ACT_FAMILIAR)) { pop_call(); return (UMAX(1,ch->learned[sn])); } if ((sn == gsn_evasion || sn == gsn_imp_evasion) && IS_ACT(ch, ACT_COMPANION|ACT_WARHORSE)) { if (ch->level > 11 && sn == gsn_imp_evasion) { pop_call(); return (UMAX(1,ch->learned[sn])); } if (ch->level > 4 && sn == gsn_evasion) { pop_call(); return (UMAX(1,ch->learned[sn])); } } if (ch->learned[sn] <= 0) { pop_call(); return 0; } pop_call(); return (ch->learned[sn]); } /* * Calculate the effective armor type worn by a character. */ int armor_type_worn( CHAR_DATA *ch ) { return ch->armor_type; } /* * Check to see if an armor check penalty for non-proficiency applies - Kregor 4/7/07 */ bool has_armor_penalty(CHAR_DATA * ch) { push_call("has_armor_penalty(%p)",ch); if (armor_type_worn(ch) >= ARMOR_HEAVY) { if (!learned(ch, gsn_armor_proficiency_heavy)) { pop_call(); return TRUE; } } else if (armor_type_worn(ch) >= ARMOR_MEDIUM) { if (!learned(ch, gsn_armor_proficiency_medium)) { pop_call(); return TRUE; } } if (armor_type_worn(ch) >= ARMOR_LIGHT) { if (!learned(ch, gsn_armor_proficiency_light)) { pop_call(); return TRUE; } } pop_call(); return FALSE; } /* * return effective max dex bonus. */ int dex_bonus_max( CHAR_DATA *ch ) { int max = ch->dex_max; push_call("dex_bonus_max(%p)",ch); switch (encumberance(ch)) { case VERY_ENCUMBERED: max = UMIN(max, 1); break; case ENCUMBERED: max = UMIN(max, 3); break; } pop_call(); return (int)(max); } /* * Pet and familiar functions - Kregor */ int get_pets( CHAR_DATA *ch) { PET_DATA *pet; int cnt = 0; push_call("get_pets(%p)",ch); for (pet = mud->f_pet ; pet ; pet = pet->next) { if (pet->ch->master == ch) { if (IS_ACT(pet->ch, ACT_PET)) { cnt++; } } } pop_call(); return( cnt ); } int get_pet_levels( CHAR_DATA *ch) { PET_DATA *pet; int cnt = 0; push_call("get_pet_levels(%p)",ch); for (pet = mud->f_pet ; pet ; pet = pet->next) { if (pet->ch->master == ch) { if (IS_ACT(pet->ch, ACT_PET)) { cnt += pet->ch->level; } } } pop_call(); return( cnt ); } // total follower levels a char may have - Kregor int max_pet_levels( CHAR_DATA *ch) { int levels; push_call("max_pet_levels(%p)",ch); levels = ch->level; if (learned(ch, gsn_leadership)) levels = levels * 3 / 2; levels += stat_bonus(FALSE, ch, APPLY_CHA); pop_call(); return( levels ); } bool has_familiar(CHAR_DATA *ch, int race) { push_call("has_familiar(%p)",ch); if (IS_NPC(ch)) { pop_call(); return FALSE; } if (ch->pcdata->familiar == race) { pop_call(); return TRUE; } pop_call(); return FALSE; } bool is_master( CHAR_DATA *ch, CHAR_DATA *master ) { push_call("is_master(%p,%p)",ch,master); if ((IS_ACT(ch, ACT_PET) || IS_AFFECTED (ch, AFF_DOMINATE)) && ch->master == master) { pop_call(); return TRUE; } pop_call(); return FALSE; } /* * snarf familiar or companion for a char */ CHAR_DATA *get_familiar(CHAR_DATA *ch) { PET_DATA *pet; push_call("get_familiar(%p)",ch); for (pet = mud->f_pet ; pet ; pet = pet->next) { if (pet->ch->master == ch) { if (IS_SET(pet->ch->act, ACT_FAMILIAR)) { pop_call(); return pet->ch; } } } pop_call(); return NULL; } /* * snarf familiar or companion for a char */ CHAR_DATA *get_warhorse(CHAR_DATA *ch) { PET_DATA *pet; push_call("get_familiar(%p)",ch); for (pet = mud->f_pet ; pet ; pet = pet->next) { if (pet->ch->master == ch) { if (IS_SET(pet->ch->act, ACT_WARHORSE)) { pop_call(); return pet->ch; } } } pop_call(); return NULL; } CHAR_DATA *get_companion(CHAR_DATA *ch) { PET_DATA *pet; push_call("get_familiar(%p)",ch); for (pet = mud->f_pet ; pet ; pet = pet->next) { if (pet->ch->master == ch) { if (IS_SET(pet->ch->act, ACT_COMPANION)) { pop_call(); return pet->ch; } } } pop_call(); return NULL; }