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