summaryrefslogtreecommitdiffstats
path: root/trunk/source/player.cc
diff options
context:
space:
mode:
authorpeterb12 <peterb12@c06c8d41-db1a-0410-9941-cceddc491573>2005-07-21 02:34:44 +0000
committerpeterb12 <peterb12@c06c8d41-db1a-0410-9941-cceddc491573>2005-07-21 02:34:44 +0000
commit673bdae75485d14f759af597c3c62b99601f9a43 (patch)
tree368103f29fe0ce5dcf98060d9b5faa04590085fb /trunk/source/player.cc
parent7e900be770db24b0405fd2162491c405a425873e (diff)
downloadcrawl-ref-673bdae75485d14f759af597c3c62b99601f9a43.tar.gz
crawl-ref-673bdae75485d14f759af597c3c62b99601f9a43.zip
Initial revision
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@3 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'trunk/source/player.cc')
-rw-r--r--trunk/source/player.cc3885
1 files changed, 3885 insertions, 0 deletions
diff --git a/trunk/source/player.cc b/trunk/source/player.cc
new file mode 100644
index 0000000000..ed06fd4d18
--- /dev/null
+++ b/trunk/source/player.cc
@@ -0,0 +1,3885 @@
+/*
+ * File: player.cc
+ * Summary: Player related functions.
+ * Written by: Linley Henzell
+ *
+ * Change History (most recent first):
+ *
+ * <6> 7/30/99 BWR Added player_spell_levels()
+ * <5> 7/13/99 BWR Added player_res_electricity()
+ * and player_hunger_rate()
+ * <4> 6/22/99 BWR Racial adjustments to stealth and Armour.
+ * <3> 5/20/99 BWR Fixed problems with random stat increases, added kobold
+ * stat increase. increased EV recovery for Armour.
+ * <2> 5/08/99 LRH display_char_status correctly handles magic_contamination.
+ * <1> -/--/-- LRH Created
+ */
+
+#include "AppHdr.h"
+#include "player.h"
+
+#ifdef DOS
+#include <conio.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+
+#include "externs.h"
+
+#include "itemname.h"
+#include "macro.h"
+#include "misc.h"
+#include "mon-util.h"
+#include "mutation.h"
+#include "output.h"
+#include "randart.h"
+#include "religion.h"
+#include "skills2.h"
+#include "spl-util.h"
+#include "spells4.h"
+#include "stuff.h"
+#include "view.h"
+#include "wpn-misc.h"
+
+
+/*
+ you.duration []: //jmf: obsolete, see enum.h instead
+ 0 - liquid flames
+ 1 - icy armour
+ 2 - repel missiles
+ 3 - prayer
+ 4 - regeneration
+ 5 - vorpal blade
+ 6 - fire brand
+ 7 - ice brand
+ 8 - lethal infusion
+ 9 - swiftness
+ 10 - insulation
+ 11 - stonemail
+ 12 - controlled flight
+ 13 - teleport
+ 14 - control teleport
+ 15 - poison weapon
+ 16 - resist poison
+ 17 - breathe something
+ 18 - transformation (duration)
+ 19 - death channel
+ 20 - deflect missiles
+ */
+
+/* attributes
+ 0 - resist lightning
+ 1 - spec_air
+ 2 - spec_earth
+ 3 - control teleport
+ 4 - walk slowly (eg naga)
+ 5 - transformation (form)
+ 6 - Nemelex card gift countdown
+ 7 - Nemelex has given you a card table
+ 8 - How many demonic powers a dspawn has
+ */
+
+/* armour list
+ 0 - wielded
+ 1 - cloak
+ 2 - helmet
+ 3 - gloves
+ 4 - boots
+ 5 - shield
+ 6 - body armour
+ 7 - ring 0
+ 8 - ring 1
+ 9 - amulet
+ */
+
+/* Contains functions which return various player state vars,
+ and other stuff related to the player. */
+
+int species_exp_mod(char species);
+void ability_increase(void);
+
+//void priest_spells(int priest_pass[10], char religious); // see actual function for reasoning here {dlb}
+bool player_in_branch( int branch )
+{
+ return (you.level_type == LEVEL_DUNGEON && you.where_are_you == branch);
+}
+
+bool player_in_hell( void )
+{
+ return (you.level_type == LEVEL_DUNGEON
+ && (you.where_are_you >= BRANCH_DIS
+ && you.where_are_you <= BRANCH_THE_PIT)
+ && you.where_are_you != BRANCH_VESTIBULE_OF_HELL);
+}
+
+bool player_in_water(void)
+{
+ return (!player_is_levitating()
+ && (grd[you.x_pos][you.y_pos] == DNGN_DEEP_WATER
+ || grd[you.x_pos][you.y_pos] == DNGN_SHALLOW_WATER));
+}
+
+bool player_is_swimming(void)
+{
+ return (player_in_water() && you.species == SP_MERFOLK);
+}
+
+bool player_under_penance(void)
+{
+ if (you.religion != GOD_NO_GOD)
+ return (you.penance[you.religion]);
+ else
+ return (false);
+}
+
+bool player_genus(unsigned char which_genus, unsigned char species)
+{
+ if (species == SP_UNKNOWN)
+ species = you.species;
+
+ switch (species)
+ {
+ case SP_RED_DRACONIAN:
+ case SP_WHITE_DRACONIAN:
+ case SP_GREEN_DRACONIAN:
+ case SP_GOLDEN_DRACONIAN:
+ case SP_GREY_DRACONIAN:
+ case SP_BLACK_DRACONIAN:
+ case SP_PURPLE_DRACONIAN:
+ case SP_MOTTLED_DRACONIAN:
+ case SP_PALE_DRACONIAN:
+ case SP_UNK0_DRACONIAN:
+ case SP_UNK1_DRACONIAN:
+ case SP_UNK2_DRACONIAN:
+ return (which_genus == GENPC_DRACONIAN);
+
+ case SP_ELF:
+ case SP_HIGH_ELF:
+ case SP_GREY_ELF:
+ case SP_DEEP_ELF:
+ case SP_SLUDGE_ELF:
+ return (which_genus == GENPC_ELVEN);
+
+ case SP_HILL_DWARF:
+ case SP_MOUNTAIN_DWARF:
+ return (which_genus == GENPC_DWARVEN);
+
+ default:
+ break;
+ }
+
+ return (false);
+} // end player_genus()
+
+// Looks in equipment "slot" to see if there is an equiped "sub_type".
+// Returns number of matches (in the case of rings, both are checked)
+int player_equip( int slot, int sub_type )
+{
+ int ret = 0;
+
+ switch (slot)
+ {
+ case EQ_WEAPON:
+ // Hands can have more than just weapons.
+ if (you.equip[EQ_WEAPON] != -1
+ && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_WEAPONS
+ && you.inv[you.equip[EQ_WEAPON]].sub_type == sub_type)
+ {
+ ret++;
+ }
+ break;
+
+ case EQ_STAFF:
+ // Like above, but must be magical stave.
+ if (you.equip[EQ_WEAPON] != -1
+ && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_STAVES
+ && you.inv[you.equip[EQ_WEAPON]].sub_type == sub_type)
+ {
+ ret++;
+ }
+ break;
+
+ case EQ_RINGS:
+ if (you.equip[EQ_LEFT_RING] != -1
+ && you.inv[you.equip[EQ_LEFT_RING]].sub_type == sub_type)
+ {
+ ret++;
+ }
+
+ if (you.equip[EQ_RIGHT_RING] != -1
+ && you.inv[you.equip[EQ_RIGHT_RING]].sub_type == sub_type)
+ {
+ ret++;
+ }
+ break;
+
+ case EQ_RINGS_PLUS:
+ if (you.equip[EQ_LEFT_RING] != -1
+ && you.inv[you.equip[EQ_LEFT_RING]].sub_type == sub_type)
+ {
+ ret += you.inv[you.equip[EQ_LEFT_RING]].plus;
+ }
+
+ if (you.equip[EQ_RIGHT_RING] != -1
+ && you.inv[you.equip[EQ_RIGHT_RING]].sub_type == sub_type)
+ {
+ ret += you.inv[you.equip[EQ_RIGHT_RING]].plus;
+ }
+ break;
+
+ case EQ_RINGS_PLUS2:
+ if (you.equip[EQ_LEFT_RING] != -1
+ && you.inv[you.equip[EQ_LEFT_RING]].sub_type == sub_type)
+ {
+ ret += you.inv[you.equip[EQ_LEFT_RING]].plus2;
+ }
+
+ if (you.equip[EQ_RIGHT_RING] != -1
+ && you.inv[you.equip[EQ_RIGHT_RING]].sub_type == sub_type)
+ {
+ ret += you.inv[you.equip[EQ_RIGHT_RING]].plus2;
+ }
+ break;
+
+ case EQ_ALL_ARMOUR:
+ // doesn't make much sense here... be specific. -- bwr
+ break;
+
+ default:
+ if (you.equip[slot] != -1
+ && you.inv[you.equip[slot]].sub_type == sub_type)
+ {
+ ret++;
+ }
+ break;
+ }
+
+ return (ret);
+}
+
+
+// Looks in equipment "slot" to see if equiped item has "special" ego-type
+// Returns number of matches (jewellery returns zero -- no ego type).
+int player_equip_ego_type( int slot, int special )
+{
+ int ret = 0;
+ int wpn;
+
+ switch (slot)
+ {
+ case EQ_WEAPON:
+ // This actually checks against the "branding", so it will catch
+ // randart brands, but not fixed artefacts. -- bwr
+
+ // Hands can have more than just weapons.
+ wpn = you.equip[EQ_WEAPON];
+ if (wpn != -1
+ && you.inv[wpn].base_type == OBJ_WEAPONS
+ && get_weapon_brand( you.inv[wpn] ) == special)
+ {
+ ret++;
+ }
+ break;
+
+ case EQ_LEFT_RING:
+ case EQ_RIGHT_RING:
+ case EQ_AMULET:
+ case EQ_STAFF:
+ case EQ_RINGS:
+ case EQ_RINGS_PLUS:
+ case EQ_RINGS_PLUS2:
+ // no ego types for these slots
+ break;
+
+ case EQ_ALL_ARMOUR:
+ // Check all armour slots:
+ for (int i = EQ_CLOAK; i <= EQ_BODY_ARMOUR; i++)
+ {
+ if (you.equip[i] != -1
+ && get_armour_ego_type( you.inv[you.equip[i]] ) == special)
+ {
+ ret++;
+ }
+ }
+ break;
+
+ default:
+ // Check a specific armour slot for an ego type:
+ if (you.equip[slot] != -1
+ && get_armour_ego_type( you.inv[you.equip[slot]] ) == special)
+ {
+ ret++;
+ }
+ break;
+ }
+
+ return (ret);
+}
+
+int player_damage_type( void )
+{
+ const int wpn = you.equip[ EQ_WEAPON ];
+
+ if (wpn != -1)
+ {
+ return (damage_type( you.inv[wpn].base_type, you.inv[wpn].sub_type ));
+ }
+ else if (you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS
+ || you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON
+ || you.mutation[MUT_CLAWS]
+ || you.species == SP_TROLL
+ || you.species == SP_GHOUL)
+ {
+ return (DVORP_SLICING);
+ }
+
+ return (DVORP_CRUSHING);
+}
+
+// returns band of player's melee damage
+int player_damage_brand( void )
+{
+ int ret = SPWPN_NORMAL;
+ const int wpn = you.equip[ EQ_WEAPON ];
+
+ if (wpn != -1)
+ ret = get_weapon_brand( you.inv[wpn] );
+ else if (you.confusing_touch)
+ ret = SPWPN_CONFUSE;
+ else if (you.mutation[MUT_DRAIN_LIFE])
+ ret = SPWPN_DRAINING;
+ else
+ {
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_SPIDER:
+ ret = SPWPN_VENOM;
+ break;
+
+ case TRAN_ICE_BEAST:
+ ret = SPWPN_FREEZING;
+ break;
+
+ case TRAN_LICH:
+ ret = SPWPN_DRAINING;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return (ret);
+}
+
+int player_teleport(void)
+{
+ int tp = 0;
+
+ /* rings */
+ tp += 8 * player_equip( EQ_RINGS, RING_TELEPORTATION );
+
+ /* mutations */
+ tp += you.mutation[MUT_TELEPORT] * 3;
+
+ /* randart weapons only */
+ if (you.equip[EQ_WEAPON] != -1
+ && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_WEAPONS
+ && is_random_artefact( you.inv[you.equip[EQ_WEAPON]] ))
+ {
+ tp += scan_randarts(RAP_CAUSE_TELEPORTATION);
+ }
+
+ return tp;
+} // end player_teleport()
+
+int player_regen(void)
+{
+ int rr = you.hp_max / 3;
+
+ if (rr > 20)
+ rr = 20 + ((rr - 20) / 2);
+
+ /* rings */
+ rr += 40 * player_equip( EQ_RINGS, RING_REGENERATION );
+
+ /* spell */
+ if (you.duration[DUR_REGENERATION])
+ rr += 100;
+
+ /* troll or troll leather -- trolls can't get both */
+ if (you.species == SP_TROLL)
+ rr += 40;
+ else if (player_equip( EQ_BODY_ARMOUR, ARM_TROLL_LEATHER_ARMOUR ))
+ rr += 30;
+
+ /* fast heal mutation */
+ rr += you.mutation[MUT_REGENERATION] * 20;
+
+ /* ghouls heal slowly */
+ // dematerialized people heal slowly
+ // dematerialized ghouls shouldn't heal any more slowly -- bwr
+ if ((you.species == SP_GHOUL
+ && (you.attribute[ATTR_TRANSFORMATION] == TRAN_NONE
+ || you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS))
+ || you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR)
+ {
+ rr /= 2;
+ }
+
+ if (rr < 1)
+ rr = 1;
+
+ return (rr);
+}
+
+int player_hunger_rate(void)
+{
+ int hunger = 3;
+
+ // jmf: hunger isn't fair while you can't eat
+ // Actually, it is since you can detransform any time you like -- bwr
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR)
+ return 0;
+
+ switch (you.species)
+ {
+ case SP_HALFLING:
+ case SP_SPRIGGAN:
+ hunger--;
+ break;
+
+ case SP_OGRE:
+ case SP_OGRE_MAGE:
+ case SP_DEMIGOD:
+ hunger++;
+ break;
+
+ case SP_CENTAUR:
+ hunger += 2;
+ break;
+
+ case SP_TROLL:
+ hunger += 6;
+ break;
+ }
+
+ if (you.duration[DUR_REGENERATION] > 0)
+ hunger += 4;
+
+ // moved here from acr.cc... maintaining the >= 40 behaviour
+ if (you.hunger >= 40)
+ {
+ if (you.invis > 0)
+ hunger += 5;
+
+ // berserk has its own food penalty -- excluding berserk haste
+ if (you.haste > 0 && !you.berserker)
+ hunger += 5;
+ }
+
+ hunger += you.mutation[MUT_FAST_METABOLISM];
+
+ if (you.mutation[MUT_SLOW_METABOLISM] > 2)
+ hunger -= 2;
+ else if (you.mutation[MUT_SLOW_METABOLISM] > 0)
+ hunger--;
+
+ // rings
+ hunger += 2 * player_equip( EQ_RINGS, RING_REGENERATION );
+ hunger += 4 * player_equip( EQ_RINGS, RING_HUNGER );
+ hunger -= 2 * player_equip( EQ_RINGS, RING_SUSTENANCE );
+
+ // weapon ego types
+ hunger += 6 * player_equip_ego_type( EQ_WEAPON, SPWPN_VAMPIRICISM );
+ hunger += 9 * player_equip_ego_type( EQ_WEAPON, SPWPN_VAMPIRES_TOOTH );
+
+ // troll leather armour
+ hunger += player_equip( EQ_BODY_ARMOUR, ARM_TROLL_LEATHER_ARMOUR );
+
+ // randarts
+ hunger += scan_randarts(RAP_METABOLISM);
+
+ // burden
+ hunger += you.burden_state;
+
+ if (hunger < 1)
+ hunger = 1;
+
+ return (hunger);
+}
+
+int player_spell_levels(void)
+{
+ int sl = (you.experience_level - 1) + (you.skills[SK_SPELLCASTING] * 2);
+
+ bool fireball = false;
+ bool delayed_fireball = false;
+
+ if (sl > 99)
+ sl = 99;
+
+ for (int i = 0; i < 25; i++)
+ {
+ if (you.spells[i] == SPELL_FIREBALL)
+ fireball = true;
+ else if (you.spells[i] == SPELL_DELAYED_FIREBALL)
+ delayed_fireball = true;
+
+ if (you.spells[i] != SPELL_NO_SPELL)
+ sl -= spell_difficulty(you.spells[i]);
+ }
+
+ // Fireball is free for characters with delayed fireball
+ if (fireball && delayed_fireball)
+ sl += spell_difficulty( SPELL_FIREBALL );
+
+ // Note: This can happen because of level drain. Maybe we should
+ // force random spells out when that happens. -- bwr
+ if (sl < 0)
+ sl = 0;
+
+ return (sl);
+}
+
+int player_res_magic(void)
+{
+ int rm = 0;
+
+ switch (you.species)
+ {
+ default:
+ rm = you.experience_level * 3;
+ break;
+ case SP_HIGH_ELF:
+ case SP_GREY_ELF:
+ case SP_ELF:
+ case SP_SLUDGE_ELF:
+ case SP_HILL_DWARF:
+ case SP_MOUNTAIN_DWARF:
+ rm = you.experience_level * 4;
+ break;
+ case SP_NAGA:
+ rm = you.experience_level * 5;
+ break;
+ case SP_PURPLE_DRACONIAN:
+ case SP_GNOME:
+ case SP_DEEP_ELF:
+ rm = you.experience_level * 6;
+ break;
+ case SP_SPRIGGAN:
+ rm = you.experience_level * 7;
+ break;
+ }
+
+ /* armour */
+ rm += 30 * player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_MAGIC_RESISTANCE );
+
+ /* rings of magic resistance */
+ rm += 40 * player_equip( EQ_RINGS, RING_PROTECTION_FROM_MAGIC );
+
+ /* randarts */
+ rm += scan_randarts(RAP_MAGIC);
+
+ /* Enchantment skill */
+ rm += 2 * you.skills[SK_ENCHANTMENTS];
+
+ /* Mutations */
+ rm += 30 * you.mutation[MUT_MAGIC_RESISTANCE];
+
+ /* transformations */
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_LICH)
+ rm += 50;
+
+ return rm;
+}
+
+int player_res_fire(void)
+{
+ int rf = 0;
+
+ /* rings of fire resistance/fire */
+ rf += player_equip( EQ_RINGS, RING_PROTECTION_FROM_FIRE );
+ rf += player_equip( EQ_RINGS, RING_FIRE );
+
+ /* rings of ice */
+ rf -= player_equip( EQ_RINGS, RING_ICE );
+
+ /* Staves */
+ rf += player_equip( EQ_STAFF, STAFF_FIRE );
+
+ // body armour:
+ rf += 2 * player_equip( EQ_BODY_ARMOUR, ARM_DRAGON_ARMOUR );
+ rf += player_equip( EQ_BODY_ARMOUR, ARM_GOLD_DRAGON_ARMOUR );
+ rf -= player_equip( EQ_BODY_ARMOUR, ARM_ICE_DRAGON_ARMOUR );
+
+ // ego armours
+ rf += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_FIRE_RESISTANCE );
+ rf += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_RESISTANCE );
+
+ // randart weapons:
+ rf += scan_randarts(RAP_FIRE);
+
+ // species:
+ if (you.species == SP_MUMMY)
+ rf--;
+ else if (you.species == SP_RED_DRACONIAN && you.experience_level > 17)
+ rf++;
+
+ // mutations:
+ rf += you.mutation[MUT_HEAT_RESISTANCE];
+
+ if (you.fire_shield)
+ rf += 2;
+
+ // transformations:
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_ICE_BEAST:
+ rf--;
+ break;
+ case TRAN_DRAGON:
+ rf += 2;
+ break;
+ case TRAN_SERPENT_OF_HELL:
+ rf += 2;
+ break;
+ case TRAN_AIR:
+ rf -= 2;
+ break;
+ }
+
+ if (rf < -3)
+ rf = -3;
+ else if (rf > 3)
+ rf = 3;
+
+ return (rf);
+}
+
+int player_res_cold(void)
+{
+ int rc = 0;
+
+ /* rings of fire resistance/fire */
+ rc += player_equip( EQ_RINGS, RING_PROTECTION_FROM_COLD );
+ rc += player_equip( EQ_RINGS, RING_ICE );
+
+ /* rings of ice */
+ rc -= player_equip( EQ_RINGS, RING_FIRE );
+
+ /* Staves */
+ rc += player_equip( EQ_STAFF, STAFF_COLD );
+
+ // body armour:
+ rc += 2 * player_equip( EQ_BODY_ARMOUR, ARM_ICE_DRAGON_ARMOUR );
+ rc += player_equip( EQ_BODY_ARMOUR, ARM_GOLD_DRAGON_ARMOUR );
+ rc -= player_equip( EQ_BODY_ARMOUR, ARM_DRAGON_ARMOUR );
+
+ // ego armours
+ rc += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_COLD_RESISTANCE );
+ rc += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_RESISTANCE );
+
+ // randart weapons:
+ rc += scan_randarts(RAP_COLD);
+
+ // species:
+ if (you.species == SP_MUMMY || you.species == SP_GHOUL)
+ rc++;
+ else if (you.species == SP_WHITE_DRACONIAN && you.experience_level > 17)
+ rc++;
+
+ // mutations:
+ rc += you.mutation[MUT_COLD_RESISTANCE];
+
+ if (you.fire_shield)
+ rc -= 2;
+
+ // transformations:
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_ICE_BEAST:
+ rc += 3;
+ break;
+ case TRAN_DRAGON:
+ rc--;
+ break;
+ case TRAN_LICH:
+ rc++;
+ break;
+ case TRAN_AIR:
+ rc -= 2;
+ break;
+ }
+
+ if (rc < -3)
+ rc = -3;
+ else if (rc > 3)
+ rc = 3;
+
+ return (rc);
+}
+
+int player_res_electricity(void)
+{
+ int re = 0;
+
+ if (you.duration[DUR_INSULATION])
+ re++;
+
+ // staff
+ re += player_equip( EQ_STAFF, STAFF_AIR );
+
+ // body armour:
+ re += player_equip( EQ_BODY_ARMOUR, ARM_STORM_DRAGON_ARMOUR );
+
+ // randart weapons:
+ re += scan_randarts(RAP_ELECTRICITY);
+
+ // species:
+ if (you.species == SP_BLACK_DRACONIAN && you.experience_level > 17)
+ re++;
+
+ // mutations:
+ if (you.mutation[MUT_SHOCK_RESISTANCE])
+ re++;
+
+ // transformations:
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_STATUE)
+ re += 1;
+
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR)
+ re += 2; // mutliple levels currently meaningless
+
+ if (you.attribute[ATTR_DIVINE_LIGHTNING_PROTECTION] > 0)
+ re = 3;
+ else if (re > 1)
+ re = 1;
+
+ return (re);
+} // end player_res_electricity()
+
+// funny that no races are susceptible to poisons {dlb}
+int player_res_poison(void)
+{
+ int rp = 0;
+
+ /* rings of poison resistance */
+ rp += player_equip( EQ_RINGS, RING_POISON_RESISTANCE );
+
+ /* Staves */
+ rp += player_equip( EQ_STAFF, STAFF_POISON );
+
+ /* the staff of Olgreb: */
+ if (you.equip[EQ_WEAPON] != -1
+ && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_WEAPONS
+ && you.inv[you.equip[EQ_WEAPON]].special == SPWPN_STAFF_OF_OLGREB)
+ {
+ rp++;
+ }
+
+ // ego armour:
+ rp += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_POISON_RESISTANCE );
+
+ // body armour:
+ rp += player_equip( EQ_BODY_ARMOUR, ARM_GOLD_DRAGON_ARMOUR );
+ rp += player_equip( EQ_BODY_ARMOUR, ARM_SWAMP_DRAGON_ARMOUR );
+
+ // spells:
+ if (you.duration[DUR_RESIST_POISON] > 0)
+ rp++;
+
+ // randart weapons:
+ rp += scan_randarts(RAP_POISON);
+
+ // species:
+ if (you.species == SP_MUMMY || you.species == SP_NAGA
+ || you.species == SP_GHOUL
+ || (you.species == SP_GREEN_DRACONIAN && you.experience_level > 6))
+ {
+ rp++;
+ }
+
+ // mutations:
+ rp += you.mutation[MUT_POISON_RESISTANCE];
+
+ // transformations:
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_LICH:
+ case TRAN_ICE_BEAST:
+ case TRAN_STATUE:
+ case TRAN_DRAGON:
+ case TRAN_SERPENT_OF_HELL:
+ case TRAN_AIR:
+ rp++;
+ break;
+ }
+
+ if (rp > 3)
+ rp = 3;
+
+ return (rp);
+} // end player_res_poison()
+
+unsigned char player_spec_death(void)
+{
+ int sd = 0;
+
+ /* Staves */
+ sd += player_equip( EQ_STAFF, STAFF_DEATH );
+
+ // body armour:
+ if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI ))
+ sd++;
+
+ // species:
+ if (you.species == SP_MUMMY)
+ {
+ if (you.experience_level >= 13)
+ sd++;
+ if (you.experience_level >= 26)
+ sd++;
+ }
+
+ // transformations:
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_LICH)
+ sd++;
+
+ return sd;
+}
+
+unsigned char player_spec_holy(void)
+{
+ //if ( you.char_class == JOB_PRIEST || you.char_class == JOB_PALADIN )
+ // return 1;
+ return 0;
+}
+
+unsigned char player_spec_fire(void)
+{
+ int sf = 0;
+
+ // staves:
+ sf += player_equip( EQ_STAFF, STAFF_FIRE );
+
+ // rings of fire:
+ sf += player_equip( EQ_RINGS, RING_FIRE );
+
+ if (you.fire_shield)
+ sf++;
+
+ return sf;
+}
+
+unsigned char player_spec_cold(void)
+{
+ int sc = 0;
+
+ // staves:
+ sc += player_equip( EQ_STAFF, STAFF_COLD );
+
+ // rings of ice:
+ sc += player_equip( EQ_RINGS, RING_ICE );
+
+ return sc;
+}
+
+unsigned char player_spec_earth(void)
+{
+ int se = 0;
+
+ /* Staves */
+ se += player_equip( EQ_STAFF, STAFF_EARTH );
+
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR)
+ se--;
+
+ return se;
+}
+
+unsigned char player_spec_air(void)
+{
+ int sa = 0;
+
+ /* Staves */
+ sa += player_equip( EQ_STAFF, STAFF_AIR );
+
+ //jmf: this was too good
+ //if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR)
+ // sa++;
+ return sa;
+}
+
+unsigned char player_spec_conj(void)
+{
+ int sc = 0;
+
+ /* Staves */
+ sc += player_equip( EQ_STAFF, STAFF_CONJURATION );
+
+ // armour of the Archmagi
+ if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI ))
+ sc++;
+
+ return sc;
+}
+
+unsigned char player_spec_ench(void)
+{
+ int se = 0;
+
+ /* Staves */
+ se += player_equip( EQ_STAFF, STAFF_ENCHANTMENT );
+
+ // armour of the Archmagi
+ if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI ))
+ se++;
+
+ return se;
+}
+
+unsigned char player_spec_summ(void)
+{
+ int ss = 0;
+
+ /* Staves */
+ ss += player_equip( EQ_STAFF, STAFF_SUMMONING );
+
+ // armour of the Archmagi
+ if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI ))
+ ss++;
+
+ return ss;
+}
+
+unsigned char player_spec_poison(void)
+{
+ int sp = 0;
+
+ /* Staves */
+ sp += player_equip( EQ_STAFF, STAFF_POISON );
+
+ if (you.equip[EQ_WEAPON] != -1
+ && you.inv[you.equip[EQ_WEAPON]].base_type == OBJ_WEAPONS
+ && you.inv[you.equip[EQ_WEAPON]].special == SPWPN_STAFF_OF_OLGREB)
+ {
+ sp++;
+ }
+
+ return sp;
+}
+
+unsigned char player_energy(void)
+{
+ unsigned char pe = 0;
+
+ // Staves
+ pe += player_equip( EQ_STAFF, STAFF_ENERGY );
+
+ return pe;
+}
+
+int player_prot_life(void)
+{
+ int pl = 0;
+
+ // rings
+ pl += player_equip( EQ_RINGS, RING_LIFE_PROTECTION );
+
+ // armour: (checks body armour only)
+ pl += player_equip_ego_type( EQ_ALL_ARMOUR, SPARM_POSITIVE_ENERGY );
+
+ if (you.is_undead)
+ pl += 3;
+
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_STATUE:
+ pl += 1;
+ break;
+
+ case TRAN_SERPENT_OF_HELL:
+ pl += 2;
+ break;
+
+ case TRAN_LICH:
+ pl += 3;
+ break;
+
+ default:
+ break;
+ }
+
+ // randart wpns
+ pl += scan_randarts(RAP_NEGATIVE_ENERGY);
+
+ // demonic power
+ pl += you.mutation[MUT_NEGATIVE_ENERGY_RESISTANCE];
+
+ if (pl > 3)
+ pl = 3;
+
+ return (pl);
+}
+
+// New player movement speed system... allows for a bit more that
+// "player runs fast" and "player walks slow" in that the speed is
+// actually calculated (allowing for centaurs to get a bonus from
+// swiftness and other such things). Levels of the mutation now
+// also have meaning (before they all just meant fast). Most of
+// this isn't as fast as it used to be (6 for having anything), but
+// even a slight speed advantage is very good... and we certainly don't
+// want to go past 6 (see below). -- bwr
+int player_movement_speed(void)
+{
+ int mv = 10;
+
+ if (you.species == SP_MERFOLK && player_is_swimming())
+ {
+ // This is swimming... so it doesn't make sense to really
+ // apply the other things (the mutation is "cover ground",
+ // swiftness is an air spell, can't wear boots, can't be
+ // transformed).
+ mv = 6;
+ }
+ else
+ {
+ /* transformations */
+ if (!player_is_shapechanged())
+ {
+ // Centaurs and spriggans are only fast in their regular
+ // shape (ie untransformed, blade hands, lich form)
+ if (you.species == SP_CENTAUR)
+ mv = 8;
+ else if (you.species == SP_SPRIGGAN)
+ mv = 6;
+ }
+ else if (you.attribute[ATTR_TRANSFORMATION] == TRAN_SPIDER)
+ mv = 8;
+
+ /* armour */
+ if (player_equip_ego_type( EQ_BOOTS, SPARM_RUNNING ))
+ mv -= 2;
+
+ if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_PONDEROUSNESS ))
+ mv += 2;
+
+ // Swiftness is an Air spell, it doesn't work in water...
+ // but levitating players will move faster. -- bwr
+ if (you.duration[DUR_SWIFTNESS] > 0 && !player_in_water())
+ mv -= (player_is_levitating() ? 4 : 2);
+
+ /* Mutations: -2, -3, -4 */
+ if (you.mutation[MUT_FAST] > 0)
+ mv -= (you.mutation[MUT_FAST] + 1);
+
+ // Burden
+ if (you.burden_state == BS_ENCUMBERED)
+ mv += 1;
+ else if (you.burden_state == BS_OVERLOADED)
+ mv += 3;
+ }
+
+ // We'll use the old value of six as a minimum, with haste this could
+ // end up as a speed of three, which is about as fast as we want
+ // the player to be able to go (since 3 is 3.33x as fast and 2 is 5x,
+ // which is a bit of a jump, and a bit too fast) -- bwr
+ if (mv < 6)
+ mv = 6;
+
+ // Nagas move slowly:
+ if (you.species == SP_NAGA && !player_is_shapechanged())
+ {
+ mv *= 14;
+ mv /= 10;
+ }
+
+ return (mv);
+}
+
+// This function differs from the above in that it's used to set the
+// initial time_taken value for the turn. Everything else (movement,
+// spellcasting, combat) applies a ratio to this value.
+int player_speed(void)
+{
+ int ps = 10;
+
+ if (you.haste)
+ ps /= 2;
+
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_STATUE:
+ ps *= 15;
+ ps /= 10;
+ break;
+
+ case TRAN_SERPENT_OF_HELL:
+ ps *= 12;
+ ps /= 10;
+ break;
+
+ default:
+ break;
+ }
+
+ return ps;
+}
+
+int player_AC(void)
+{
+ int AC = 0;
+ int i; // loop variable
+
+ // get the armour race value that corresponds to the character's race:
+ const unsigned long racial_type
+ = ((player_genus(GENPC_DWARVEN)) ? ISFLAG_DWARVEN :
+ (player_genus(GENPC_ELVEN)) ? ISFLAG_ELVEN :
+ (you.species == SP_HILL_ORC) ? ISFLAG_ORCISH
+ : 0);
+
+ for (i = EQ_CLOAK; i <= EQ_BODY_ARMOUR; i++)
+ {
+ const int item = you.equip[i];
+
+ if (item == -1 || i == EQ_SHIELD)
+ continue;
+
+ AC += you.inv[ item ].plus;
+
+ // Note that helms and boots have a sub-sub classing system
+ // which uses "plus2"... since not all members have the same
+ // AC value, we use special cases. -- bwr
+ if (i == EQ_HELMET
+ && (cmp_helmet_type( you.inv[ item ], THELM_CAP )
+ || cmp_helmet_type( you.inv[ item ], THELM_WIZARD_HAT )
+ || cmp_helmet_type( you.inv[ item ], THELM_SPECIAL )))
+ {
+ continue;
+ }
+
+ if (i == EQ_BOOTS
+ && (you.inv[ item ].plus2 == TBOOT_NAGA_BARDING
+ || you.inv[ item ].plus2 == TBOOT_CENTAUR_BARDING))
+ {
+ AC += 3;
+ }
+
+ int racial_bonus = 0; // additional levels of armour skill
+ const unsigned long armour_race = get_equip_race( you.inv[ item ] );
+ const int ac_value = property( you.inv[ item ], PARM_AC );
+
+ // Dwarven armour is universally good -- bwr
+ if (armour_race == ISFLAG_DWARVEN)
+ racial_bonus += 2;
+
+ if (racial_type && armour_race == racial_type)
+ {
+ // Elven armour is light, but still gives one level
+ // to elves. Orcish and Dwarven armour are worth +2
+ // to the correct species, plus the plus that anyone
+ // gets with dwarven armour. -- bwr
+
+ if (racial_type == ISFLAG_ELVEN)
+ racial_bonus++;
+ else
+ racial_bonus += 2;
+ }
+
+ AC += ac_value * (15 + you.skills[SK_ARMOUR] + racial_bonus) / 15;
+
+ /* Nagas/Centaurs/the deformed don't fit into body armour very well */
+ if ((you.species == SP_NAGA || you.species == SP_CENTAUR
+ || you.mutation[MUT_DEFORMED] > 0) && i == EQ_BODY_ARMOUR)
+ {
+ AC -= ac_value / 2;
+ }
+ }
+
+ AC += player_equip( EQ_RINGS_PLUS, RING_PROTECTION );
+
+ if (player_equip_ego_type( EQ_WEAPON, SPWPN_PROTECTION ))
+ AC += 5;
+
+ if (player_equip_ego_type( EQ_SHIELD, SPARM_PROTECTION ))
+ AC += 3;
+
+ AC += scan_randarts(RAP_AC);
+
+ if (you.duration[DUR_ICY_ARMOUR])
+ AC += 4 + you.skills[SK_ICE_MAGIC] / 3; // max 13
+
+ if (you.duration[DUR_STONEMAIL])
+ AC += 5 + you.skills[SK_EARTH_MAGIC] / 2; // max 18
+
+ if (you.duration[DUR_STONESKIN])
+ AC += 2 + you.skills[SK_EARTH_MAGIC] / 5; // max 7
+
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_NONE
+ || you.attribute[ATTR_TRANSFORMATION] == TRAN_LICH
+ || you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS)
+ {
+ // Being a lich doesn't preclude the benefits of hide/scales -- bwr
+ //
+ // Note: Even though necromutation is a high level spell, it does
+ // allow the character full armour (so the bonus is low). -- bwr
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_LICH)
+ AC += (3 + you.skills[SK_NECROMANCY] / 6); // max 7
+
+ //jmf: only give:
+ if (player_genus(GENPC_DRACONIAN))
+ {
+ if (you.experience_level < 8)
+ AC += 2;
+ else if (you.species == SP_GREY_DRACONIAN)
+ AC += 1 + (you.experience_level - 4) / 2; // max 12
+ else
+ AC += 1 + (you.experience_level / 4); // max 7
+ }
+ else
+ {
+ switch (you.species)
+ {
+ case SP_NAGA:
+ AC += you.experience_level / 3; // max 9
+ break;
+
+ case SP_OGRE:
+ AC++;
+ break;
+
+ case SP_TROLL:
+ case SP_CENTAUR:
+ AC += 3;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // Scales -- some evil uses of the fact that boolean "true" == 1...
+ // I'll spell things out with some comments -- bwr
+
+ // mutations:
+ // these give: +1, +2, +3
+ AC += you.mutation[MUT_TOUGH_SKIN];
+ AC += you.mutation[MUT_GREY_SCALES];
+ AC += you.mutation[MUT_SPECKLED_SCALES];
+ AC += you.mutation[MUT_IRIDESCENT_SCALES];
+ AC += you.mutation[MUT_PATTERNED_SCALES];
+ AC += you.mutation[MUT_BLUE_SCALES];
+
+ // these gives: +1, +3, +5
+ if (you.mutation[MUT_GREEN_SCALES] > 0)
+ AC += (you.mutation[MUT_GREEN_SCALES] * 2) - 1;
+ if (you.mutation[MUT_NACREOUS_SCALES] > 0)
+ AC += (you.mutation[MUT_NACREOUS_SCALES] * 2) - 1;
+ if (you.mutation[MUT_BLACK2_SCALES] > 0)
+ AC += (you.mutation[MUT_BLACK2_SCALES] * 2) - 1;
+ if (you.mutation[MUT_WHITE_SCALES] > 0)
+ AC += (you.mutation[MUT_WHITE_SCALES] * 2) - 1;
+
+ // these give: +2, +4, +6
+ AC += you.mutation[MUT_GREY2_SCALES] * 2;
+ AC += you.mutation[MUT_YELLOW_SCALES] * 2;
+ AC += you.mutation[MUT_PURPLE_SCALES] * 2;
+
+ // black gives: +3, +6, +9
+ AC += you.mutation[MUT_BLACK_SCALES] * 3;
+
+ // boney plates give: +2, +3, +4
+ if (you.mutation[MUT_BONEY_PLATES] > 0)
+ AC += you.mutation[MUT_BONEY_PLATES] + 1;
+
+ // red gives +1, +2, +4
+ AC += you.mutation[MUT_RED_SCALES]
+ + (you.mutation[MUT_RED_SCALES] == 3);
+
+ // indigo gives: +2, +3, +5
+ if (you.mutation[MUT_INDIGO_SCALES] > 0)
+ {
+ AC += 1 + you.mutation[MUT_INDIGO_SCALES]
+ + (you.mutation[MUT_INDIGO_SCALES] == 3);
+ }
+
+ // brown gives: +2, +4, +5
+ AC += (you.mutation[MUT_BROWN_SCALES] * 2)
+ - (you.mutation[MUT_METALLIC_SCALES] == 3);
+
+ // orange gives: +1, +3, +4
+ AC += you.mutation[MUT_ORANGE_SCALES]
+ + (you.mutation[MUT_ORANGE_SCALES] > 1);
+
+ // knobbly red gives: +2, +5, +7
+ AC += (you.mutation[MUT_RED2_SCALES] * 2)
+ + (you.mutation[MUT_RED2_SCALES] > 1);
+
+ // metallic gives +3, +7, +10
+ AC += you.mutation[MUT_METALLIC_SCALES] * 3
+ + (you.mutation[MUT_METALLIC_SCALES] > 1);
+ }
+ else
+ {
+ // transformations:
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_NONE:
+ case TRAN_BLADE_HANDS:
+ case TRAN_LICH: // can wear normal body armour (small bonus)
+ break;
+
+
+ case TRAN_SPIDER: // low level (small bonus), also gets EV
+ AC += (2 + you.skills[SK_POISON_MAGIC] / 6); // max 6
+ break;
+
+ case TRAN_ICE_BEAST:
+ AC += (5 + (you.skills[SK_ICE_MAGIC] + 1) / 4); // max 12
+
+ if (you.duration[DUR_ICY_ARMOUR])
+ AC += (1 + you.skills[SK_ICE_MAGIC] / 4); // max +7
+ break;
+
+ case TRAN_DRAGON:
+ AC += (7 + you.skills[SK_FIRE_MAGIC] / 3); // max 16
+ break;
+
+ case TRAN_STATUE: // main ability is armour (high bonus)
+ AC += (17 + you.skills[SK_EARTH_MAGIC] / 2); // max 30
+
+ if (you.duration[DUR_STONESKIN] || you.duration[DUR_STONEMAIL])
+ AC += (1 + you.skills[SK_EARTH_MAGIC] / 4); // max +7
+ break;
+
+ case TRAN_SERPENT_OF_HELL:
+ AC += (10 + you.skills[SK_FIRE_MAGIC] / 3); // max 19
+ break;
+
+ case TRAN_AIR: // air - scales & species ought to be irrelevent
+ AC = (you.skills[SK_AIR_MAGIC] * 3) / 2; // max 40
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return AC;
+}
+
+bool is_light_armour( const item_def &item )
+{
+ if (cmp_equip_race( item, ISFLAG_ELVEN ))
+ return (true);
+
+ switch (item.sub_type)
+ {
+ case ARM_ROBE:
+ case ARM_ANIMAL_SKIN:
+ case ARM_LEATHER_ARMOUR:
+ case ARM_STEAM_DRAGON_HIDE:
+ case ARM_STEAM_DRAGON_ARMOUR:
+ case ARM_MOTTLED_DRAGON_HIDE:
+ case ARM_MOTTLED_DRAGON_ARMOUR:
+ //case ARM_TROLL_HIDE: //jmf: these are knobbly and stiff
+ //case ARM_TROLL_LEATHER_ARMOUR:
+ return (true);
+
+ default:
+ return (false);
+ }
+}
+
+bool player_light_armour(void)
+{
+ if (you.equip[EQ_BODY_ARMOUR] == -1)
+ return true;
+
+ return (is_light_armour( you.inv[you.equip[EQ_BODY_ARMOUR]] ));
+} // end player_light_armour()
+
+//
+// This function returns true if the player has a radically different
+// shape... minor changes like blade hands don't count, also note
+// that lich transformation doesn't change the character's shape
+// (so we end up with Naga-lichs, Spiggan-lichs, Minotaur-lichs)
+// it just makes the character undead (with the benefits that implies). --bwr
+//
+bool player_is_shapechanged(void)
+{
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_NONE
+ || you.attribute[ATTR_TRANSFORMATION] == TRAN_BLADE_HANDS
+ || you.attribute[ATTR_TRANSFORMATION] == TRAN_LICH)
+ {
+ return (false);
+ }
+
+ return (true);
+}
+
+int player_evasion(void)
+{
+ int ev = 10;
+
+ int armour_ev_penalty;
+
+ if (you.equip[EQ_BODY_ARMOUR] == -1)
+ armour_ev_penalty = 0;
+ else
+ {
+ armour_ev_penalty = property( you.inv[you.equip[EQ_BODY_ARMOUR]],
+ PARM_EVASION );
+ }
+
+ // We return 2 here to give the player some chance of not being hit,
+ // repulsion fields still work while paralysed
+ if (you.paralysis)
+ return (2 + you.mutation[MUT_REPULSION_FIELD] * 2);
+
+ if (you.species == SP_CENTAUR)
+ ev -= 3;
+
+ if (you.equip[EQ_BODY_ARMOUR] != -1)
+ {
+ int ev_change = 0;
+
+ ev_change = armour_ev_penalty;
+ ev_change += you.skills[SK_ARMOUR] / 3;
+
+ if (ev_change > armour_ev_penalty / 3)
+ ev_change = armour_ev_penalty / 3;
+
+ ev += ev_change; /* remember that it's negative */
+ }
+
+ ev += player_equip( EQ_RINGS_PLUS, RING_EVASION );
+
+ if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_PONDEROUSNESS ))
+ ev -= 2;
+
+ if (you.duration[DUR_STONEMAIL])
+ ev -= 2;
+
+ if (you.duration[DUR_FORESCRY])
+ ev += 8; //jmf: is this a reasonable value?
+
+ int emod = 0;
+
+ if (!player_light_armour())
+ {
+ // meaning that the armour evasion modifier is often effectively
+ // applied twice, but not if you're wearing elven armour
+ emod += (armour_ev_penalty * 14) / 10;
+ }
+
+ emod += you.skills[SK_DODGING] / 2;
+
+ if (emod > 0)
+ ev += emod;
+
+ if (you.mutation[MUT_REPULSION_FIELD] > 0)
+ ev += (you.mutation[MUT_REPULSION_FIELD] * 2) - 1;
+
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_DRAGON:
+ ev -= 3;
+ break;
+
+ case TRAN_STATUE:
+ case TRAN_SERPENT_OF_HELL:
+ ev -= 5;
+ break;
+
+ case TRAN_SPIDER:
+ ev += 3;
+ break;
+
+ case TRAN_AIR:
+ ev += 20;
+ break;
+
+ default:
+ break;
+ }
+
+ ev += scan_randarts(RAP_EVASION);
+
+ return ev;
+} // end player_evasion()
+
+int player_magical_power( void )
+{
+ int ret = 0;
+
+ ret += 13 * player_equip( EQ_STAFF, STAFF_POWER );
+ ret += 9 * player_equip( EQ_RINGS, RING_MAGICAL_POWER );
+
+ return (ret);
+}
+
+int player_mag_abil(bool is_weighted)
+{
+ int ma = 0;
+
+ ma += 3 * player_equip( EQ_RINGS, RING_WIZARDRY );
+
+ /* Staves */
+ ma += 4 * player_equip( EQ_STAFF, STAFF_WIZARDRY );
+
+ /* armour of the Archmagi (checks body armour only) */
+ ma += 2 * player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI );
+
+ return ((is_weighted) ? ((ma * you.intel) / 10) : ma);
+} // end player_mag_abil()
+
+int player_shield_class(void) //jmf: changes for new spell
+{
+ int base_shield = 0;
+ const int shield = you.equip[EQ_SHIELD];
+
+ if (shield == -1)
+ {
+ if (!you.fire_shield && you.duration[DUR_CONDENSATION_SHIELD])
+ base_shield = 2 + (you.skills[SK_ICE_MAGIC] / 6); // max 6
+ else
+ return (0);
+ }
+ else
+ {
+ switch (you.inv[ shield ].sub_type)
+ {
+ case ARM_BUCKLER:
+ base_shield = 3; // +3/20 per skill level max 7
+ break;
+ case ARM_SHIELD:
+ base_shield = 5; // +5/20 per skill level max 11
+ break;
+ case ARM_LARGE_SHIELD:
+ base_shield = 7; // +7/20 per skill level max 16
+ break;
+ }
+
+ // bonus applied only to base, see above for effect:
+ base_shield *= (20 + you.skills[SK_SHIELDS]);
+ base_shield /= 20;
+
+ base_shield += you.inv[ shield ].plus;
+ }
+
+ return (base_shield);
+} // end player_shield_class()
+
+unsigned char player_see_invis(void)
+{
+ unsigned char si = 0;
+
+ si += player_equip( EQ_RINGS, RING_SEE_INVISIBLE );
+
+ /* armour: (checks head armour only) */
+ si += player_equip_ego_type( EQ_HELMET, SPARM_SEE_INVISIBLE );
+
+ /* Nagas & Spriggans have good eyesight */
+ if (you.species == SP_NAGA || you.species == SP_SPRIGGAN)
+ si++;
+
+ if (you.mutation[MUT_ACUTE_VISION] > 0)
+ si += you.mutation[MUT_ACUTE_VISION];
+
+ //jmf: added see_invisible spell
+ if (you.duration[DUR_SEE_INVISIBLE] > 0)
+ si++;
+
+ /* randart wpns */
+ int artefacts = scan_randarts(RAP_EYESIGHT);
+
+ if (artefacts > 0)
+ si += artefacts;
+
+ return si;
+}
+
+// This does NOT do line of sight! It checks the monster's visibility
+// with repect to the players perception, but doesn't do walls or range...
+// to find if the square the monster is in is visible see mons_near().
+bool player_monster_visible( struct monsters *mon )
+{
+ if (mons_has_ench( mon, ENCH_SUBMERGED )
+ || (mons_has_ench( mon, ENCH_INVIS ) && !player_see_invis()))
+ {
+ return (false);
+ }
+
+ return (true);
+}
+
+unsigned char player_sust_abil(void)
+{
+ unsigned char sa = 0;
+
+ sa += player_equip( EQ_RINGS, RING_SUSTAIN_ABILITIES );
+
+ return sa;
+} // end player_sust_abil()
+
+int carrying_capacity(void)
+{
+ // Originally: 1000 + you.strength * 200 + ( you.levitation ? 1000 : 0 )
+ return (3500 + (you.strength * 100) + (player_is_levitating() ? 1000 : 0));
+}
+
+int burden_change(void)
+{
+ char old_burdenstate = you.burden_state;
+
+ you.burden = 0;
+
+ int max_carried = carrying_capacity();
+
+ if (you.duration[DUR_STONEMAIL])
+ you.burden += 800;
+
+ for (unsigned char bu = 0; bu < ENDOFPACK; bu++)
+ {
+ if (you.inv[bu].quantity < 1)
+ continue;
+
+ if (you.inv[bu].base_type == OBJ_CORPSES)
+ {
+ if (you.inv[bu].sub_type == CORPSE_BODY)
+ you.burden += mons_weight(you.inv[bu].plus);
+ else if (you.inv[bu].sub_type == CORPSE_SKELETON)
+ you.burden += mons_weight(you.inv[bu].plus) / 2;
+ }
+ else
+ {
+ you.burden += mass_item( you.inv[bu] ) * you.inv[bu].quantity;
+ }
+ }
+
+ you.burden_state = BS_UNENCUMBERED;
+ set_redraw_status( REDRAW_BURDEN );
+
+ // changed the burdened levels to match the change to max_carried
+ if (you.burden < (max_carried * 5) / 6)
+ // (you.burden < max_carried - 1000)
+ {
+ you.burden_state = BS_UNENCUMBERED;
+
+ // this message may have to change, just testing {dlb}
+ if (old_burdenstate != you.burden_state)
+ mpr("Your possessions no longer seem quite so burdensome.");
+ }
+ else if (you.burden < (max_carried * 11) / 12)
+ // (you.burden < max_carried - 500)
+ {
+ you.burden_state = BS_ENCUMBERED;
+
+ if (old_burdenstate != you.burden_state)
+ mpr("You are being weighed down by all of your possessions.");
+ }
+ else
+ {
+ you.burden_state = BS_OVERLOADED;
+
+ if (old_burdenstate != you.burden_state)
+ mpr("You are being crushed by all of your possessions.");
+ }
+
+ return you.burden;
+} // end burden_change()
+
+bool you_resist_magic(int power)
+{
+ int ench_power = stepdown_value( power, 30, 40, 100, 120 );
+
+ int mrchance = 100 + player_res_magic();
+
+ mrchance -= ench_power;
+
+ int mrch2 = random2(100) + random2(101);
+
+#if DEBUG_DIAGNOSTICS
+ snprintf( info, INFO_SIZE, "Power: %d, player's MR: %d, target: %d, roll: %d",
+ ench_power, player_res_magic(), mrchance, mrch2 );
+
+ mpr( info, MSGCH_DIAGNOSTICS );
+#endif
+
+ if (mrch2 < mrchance)
+ return true; // ie saved successfully
+
+ return false;
+/* if (random2(power) / 3 + random2(power) / 3 + random2(power) / 3 >= player_res_magic()) return 0;
+ return 1; */
+}
+
+void forget_map(unsigned char chance_forgotten)
+{
+ unsigned char xcount, ycount = 0;
+
+ for (xcount = 0; xcount < GXM; xcount++)
+ {
+ for (ycount = 0; ycount < GYM; ycount++)
+ {
+ if (random2(100) < chance_forgotten)
+ {
+ env.map[xcount][ycount] = 0;
+ }
+ }
+ }
+} // end forget_map()
+
+void gain_exp( unsigned int exp_gained )
+{
+
+ if (player_equip_ego_type( EQ_BODY_ARMOUR, SPARM_ARCHMAGI )
+ && !one_chance_in(20))
+ {
+ return;
+ }
+
+#if DEBUG_DIAGNOSTICS
+ snprintf( info, INFO_SIZE, "gain_exp: %d", exp_gained );
+ mpr( info, MSGCH_DIAGNOSTICS );
+#endif
+
+ if (you.experience + exp_gained > 8999999)
+ you.experience = 8999999;
+ else
+ you.experience += exp_gained;
+
+ if (you.exp_available + exp_gained > 20000)
+ you.exp_available = 20000;
+ else
+ you.exp_available += exp_gained;
+
+ level_change();
+} // end gain_exp()
+
+void level_change(void)
+{
+ int hp_adjust = 0;
+ int mp_adjust = 0;
+
+ // necessary for the time being, as level_change() is called
+ // directly sometimes {dlb}
+ you.redraw_experience = 1;
+
+ while (you.experience_level < 27
+ && you.experience > exp_needed(you.experience_level + 2))
+ {
+ you.experience_level++;
+
+ if (you.experience_level <= you.max_level)
+ {
+ snprintf( info, INFO_SIZE, "Welcome back to level %d!",
+ you.experience_level );
+
+ mpr(info, MSGCH_INTRINSIC_GAIN);
+ more();
+
+ // Gain back the hp and mp we lose in lose_level(). -- bwr
+ inc_hp( 4, true );
+ inc_mp( 1, true );
+ }
+ else // character has gained a new level
+ {
+ snprintf( info, INFO_SIZE, "You are now a level %d %s!",
+ you.experience_level, you.class_name );
+
+ mpr(info, MSGCH_INTRINSIC_GAIN);
+ more();
+
+ int brek = 0;
+
+ if (you.experience_level > 21)
+ brek = (coinflip() ? 3 : 2);
+ else if (you.experience_level > 12)
+ brek = 3 + random2(3); // up from 2 + rand(3) -- bwr
+ else
+ brek = 4 + random2(4); // up from 3 + rand(4) -- bwr
+
+ inc_hp( brek, true );
+ inc_mp( 1, true );
+
+ if (!(you.experience_level % 3))
+ ability_increase();
+
+ switch (you.species)
+ {
+ case SP_HUMAN:
+ if (!(you.experience_level % 5))
+ modify_stat(STAT_RANDOM, 1, false);
+ break;
+
+ case SP_ELF:
+ if (you.experience_level % 3)
+ hp_adjust--;
+ else
+ mp_adjust++;
+
+ if (!(you.experience_level % 4))
+ {
+ modify_stat( (coinflip() ? STAT_INTELLIGENCE
+ : STAT_DEXTERITY), 1, false );
+ }
+ break;
+
+ case SP_HIGH_ELF:
+ if (you.experience_level == 15)
+ {
+ //jmf: got Glamour ability
+ mpr("You feel charming!", MSGCH_INTRINSIC_GAIN);
+ }
+
+ if (you.experience_level % 3)
+ hp_adjust--;
+
+ if (!(you.experience_level % 2))
+ mp_adjust++;
+
+ if (!(you.experience_level % 3))
+ {
+ modify_stat( (coinflip() ? STAT_INTELLIGENCE
+ : STAT_DEXTERITY), 1, false );
+ }
+ break;
+
+ case SP_GREY_ELF:
+ if (you.experience_level == 5)
+ {
+ //jmf: got Glamour ability
+ mpr("You feel charming!", MSGCH_INTRINSIC_GAIN);
+ mp_adjust++;
+ }
+
+ if (you.experience_level < 14)
+ hp_adjust--;
+
+ if (you.experience_level % 3)
+ mp_adjust++;
+
+ if (!(you.experience_level % 4))
+ {
+ modify_stat( (coinflip() ? STAT_INTELLIGENCE
+ : STAT_DEXTERITY), 1, false );
+ }
+
+ break;
+
+ case SP_DEEP_ELF:
+ if (you.experience_level < 17)
+ hp_adjust--;
+ if (!(you.experience_level % 3))
+ hp_adjust--;
+
+ mp_adjust++;
+
+ if (!(you.experience_level % 4))
+ modify_stat(STAT_INTELLIGENCE, 1, false);
+ break;
+
+ case SP_SLUDGE_ELF:
+ if (you.experience_level % 3)
+ hp_adjust--;
+ else
+ mp_adjust++;
+
+ if (!(you.experience_level % 4))
+ {
+ modify_stat( (coinflip() ? STAT_INTELLIGENCE
+ : STAT_DEXTERITY), 1, false );
+ }
+ break;
+
+ case SP_HILL_DWARF:
+ // lowered because of HD raise -- bwr
+ // if (you.experience_level < 14)
+ // hp_adjust++;
+
+ if (you.experience_level % 3)
+ hp_adjust++;
+
+ if (!(you.experience_level % 2))
+ mp_adjust--;
+
+ if (!(you.experience_level % 4))
+ modify_stat(STAT_STRENGTH, 1, false);
+ break;
+
+ case SP_MOUNTAIN_DWARF:
+ // lowered because of HD raise -- bwr
+ // if (you.experience_level < 14)
+ // hp_adjust++;
+
+ if (!(you.experience_level % 2))
+ hp_adjust++;
+
+ if (!(you.experience_level % 3))
+ mp_adjust--;
+
+ if (!(you.experience_level % 4))
+ modify_stat(STAT_STRENGTH, 1, false);
+ break;
+
+ case SP_HALFLING:
+ if (!(you.experience_level % 5))
+ modify_stat(STAT_DEXTERITY, 1, false);
+
+ if (you.experience_level < 17)
+ hp_adjust--;
+
+ if (!(you.experience_level % 2))
+ hp_adjust--;
+ break;
+
+ case SP_KOBOLD:
+ if (!(you.experience_level % 5))
+ {
+ modify_stat( (coinflip() ? STAT_STRENGTH
+ : STAT_DEXTERITY), 1, false );
+ }
+
+ if (you.experience_level < 17)
+ hp_adjust--;
+
+ if (!(you.experience_level % 2))
+ hp_adjust--;
+ break;
+
+ case SP_HILL_ORC:
+ // lower because of HD raise -- bwr
+ // if (you.experience_level < 17)
+ // hp_adjust++;
+
+ if (!(you.experience_level % 2))
+ hp_adjust++;
+
+ if (!(you.experience_level % 3))
+ mp_adjust--;
+
+ if (!(you.experience_level % 5))
+ modify_stat(STAT_STRENGTH, 1, false);
+ break;
+
+ case SP_MUMMY:
+ if (you.experience_level == 13 || you.experience_level == 26)
+ {
+ mpr( "You feel more in touch with the powers of death.",
+ MSGCH_INTRINSIC_GAIN );
+ }
+
+ if (you.experience_level == 13) // level 13 for now -- bwr
+ {
+ mpr( "You can now infuse your body with magic to restore decomposition.", MSGCH_INTRINSIC_GAIN );
+ }
+ break;
+
+ case SP_NAGA:
+ // lower because of HD raise -- bwr
+ // if (you.experience_level < 14)
+ // hp_adjust++;
+
+ hp_adjust++;
+
+ if (!(you.experience_level % 4))
+ modify_stat(STAT_RANDOM, 1, false);
+
+ if (!(you.experience_level % 3))
+ {
+ mpr("Your skin feels tougher.", MSGCH_INTRINSIC_GAIN);
+ you.redraw_armour_class = 1;
+ }
+ break;
+
+ case SP_GNOME:
+ if (you.experience_level < 13)
+ hp_adjust--;
+
+ if (!(you.experience_level % 3))
+ hp_adjust--;
+
+ if (!(you.experience_level % 4))
+ {
+ modify_stat( (coinflip() ? STAT_INTELLIGENCE
+ : STAT_DEXTERITY), 1, false );
+ }
+ break;
+
+ case SP_OGRE:
+ case SP_TROLL:
+ hp_adjust++;
+
+ // lowered because of HD raise -- bwr
+ // if (you.experience_level < 14)
+ // hp_adjust++;
+
+ if (!(you.experience_level % 2))
+ hp_adjust++;
+
+ if (you.experience_level % 3)
+ mp_adjust--;
+
+ if (!(you.experience_level % 3))
+ modify_stat(STAT_STRENGTH, 1, false);
+ break;
+
+ case SP_OGRE_MAGE:
+ hp_adjust++;
+
+ // lowered because of HD raise -- bwr
+ // if (you.experience_level < 14)
+ // hp_adjust++;
+
+ if (!(you.experience_level % 5))
+ {
+ modify_stat( (coinflip() ? STAT_INTELLIGENCE
+ : STAT_STRENGTH), 1, false );
+ }
+ break;
+
+ case SP_RED_DRACONIAN:
+ case SP_WHITE_DRACONIAN:
+ case SP_GREEN_DRACONIAN:
+ case SP_GOLDEN_DRACONIAN:
+/* Grey is later */
+ case SP_BLACK_DRACONIAN:
+ case SP_PURPLE_DRACONIAN:
+ case SP_MOTTLED_DRACONIAN:
+ case SP_PALE_DRACONIAN:
+ case SP_UNK0_DRACONIAN:
+ case SP_UNK1_DRACONIAN:
+ case SP_UNK2_DRACONIAN:
+ if (you.experience_level == 7)
+ {
+ switch (you.species)
+ {
+ case SP_RED_DRACONIAN:
+ mpr("Your scales start taking on a fiery red colour.",
+ MSGCH_INTRINSIC_GAIN);
+ break;
+ case SP_WHITE_DRACONIAN:
+ mpr("Your scales start taking on an icy white colour.",
+ MSGCH_INTRINSIC_GAIN);
+ break;
+ case SP_GREEN_DRACONIAN:
+ mpr("Your scales start taking on a green colour.",
+ MSGCH_INTRINSIC_GAIN);
+ mpr("You feel resistant to poison.", MSGCH_INTRINSIC_GAIN);
+ break;
+
+ case SP_GOLDEN_DRACONIAN:
+ mpr("Your scales start taking on a golden yellow colour.", MSGCH_INTRINSIC_GAIN);
+ break;
+ case SP_BLACK_DRACONIAN:
+ mpr("Your scales start turning black.", MSGCH_INTRINSIC_GAIN);
+ break;
+ case SP_PURPLE_DRACONIAN:
+ mpr("Your scales start taking on a rich purple colour.", MSGCH_INTRINSIC_GAIN);
+ break;
+ case SP_MOTTLED_DRACONIAN:
+ mpr("Your scales start taking on a weird mottled pattern.", MSGCH_INTRINSIC_GAIN);
+ break;
+ case SP_PALE_DRACONIAN:
+ mpr("Your scales start fading to a pale grey colour.", MSGCH_INTRINSIC_GAIN);
+ break;
+ case SP_UNK0_DRACONIAN:
+ case SP_UNK1_DRACONIAN:
+ case SP_UNK2_DRACONIAN:
+ mpr("");
+ break;
+ }
+ more();
+ redraw_screen();
+ }
+
+ if (you.experience_level == 18)
+ {
+ switch (you.species)
+ {
+ case SP_RED_DRACONIAN:
+ mpr("You feel resistant to fire.", MSGCH_INTRINSIC_GAIN);
+ break;
+ case SP_WHITE_DRACONIAN:
+ mpr("You feel resistant to cold.", MSGCH_INTRINSIC_GAIN);
+ break;
+ case SP_BLACK_DRACONIAN:
+ mpr("You feel resistant to electrical energy.",
+ MSGCH_INTRINSIC_GAIN);
+ break;
+ }
+ }
+
+ if (!(you.experience_level % 3))
+ hp_adjust++;
+
+ if (you.experience_level > 7 && !(you.experience_level % 4))
+ {
+ mpr("Your scales feel tougher.", MSGCH_INTRINSIC_GAIN);
+ you.redraw_armour_class = 1;
+ modify_stat(STAT_RANDOM, 1, false);
+ }
+ break;
+
+ case SP_GREY_DRACONIAN:
+ if (you.experience_level == 7)
+ {
+ mpr("Your scales start turning grey.", MSGCH_INTRINSIC_GAIN);
+ more();
+ redraw_screen();
+ }
+
+ if (!(you.experience_level % 3))
+ {
+ hp_adjust++;
+ if (you.experience_level > 7)
+ hp_adjust++;
+ }
+
+ if (you.experience_level > 7 && !(you.experience_level % 2))
+ {
+ mpr("Your scales feel tougher.", MSGCH_INTRINSIC_GAIN);
+ you.redraw_armour_class = 1;
+ }
+
+ if ((you.experience_level > 7 && !(you.experience_level % 3))
+ || you.experience_level == 4 || you.experience_level == 7)
+ {
+ modify_stat(STAT_RANDOM, 1, false);
+ }
+ break;
+
+ case SP_CENTAUR:
+ if (!(you.experience_level % 4))
+ {
+ modify_stat( (coinflip() ? STAT_DEXTERITY
+ : STAT_STRENGTH), 1, false );
+ }
+
+ // lowered because of HD raise -- bwr
+ // if (you.experience_level < 17)
+ // hp_adjust++;
+
+ if (!(you.experience_level % 2))
+ hp_adjust++;
+
+ if (!(you.experience_level % 3))
+ mp_adjust--;
+ break;
+
+ case SP_DEMIGOD:
+ if (!(you.experience_level % 3))
+ modify_stat(STAT_RANDOM, 1, false);
+
+ // lowered because of HD raise -- bwr
+ // if (you.experience_level < 17)
+ // hp_adjust++;
+
+ if (!(you.experience_level % 2))
+ hp_adjust++;
+
+ if (you.experience_level % 3)
+ mp_adjust++;
+ break;
+
+ case SP_SPRIGGAN:
+ if (you.experience_level < 17)
+ hp_adjust--;
+
+ if (you.experience_level % 3)
+ hp_adjust--;
+
+ mp_adjust++;
+
+ if (!(you.experience_level % 5))
+ {
+ modify_stat( (coinflip() ? STAT_INTELLIGENCE
+ : STAT_DEXTERITY), 1, false );
+ }
+ break;
+
+ case SP_MINOTAUR:
+ // lowered because of HD raise -- bwr
+ // if (you.experience_level < 17)
+ // hp_adjust++;
+
+ if (!(you.experience_level % 2))
+ hp_adjust++;
+
+ if (!(you.experience_level % 2))
+ mp_adjust--;
+
+ if (!(you.experience_level % 4))
+ {
+ modify_stat( (coinflip() ? STAT_DEXTERITY
+ : STAT_STRENGTH), 1, false );
+ }
+ break;
+
+ case SP_DEMONSPAWN:
+ if (you.attribute[ATTR_NUM_DEMONIC_POWERS] == 0
+ && (you.experience_level == 4
+ || (you.experience_level < 4 && one_chance_in(3))))
+ {
+ demonspawn();
+ }
+
+ if (you.attribute[ATTR_NUM_DEMONIC_POWERS] == 1
+ && you.experience_level > 4
+ && (you.experience_level == 9
+ || (you.experience_level < 9 && one_chance_in(3))))
+ {
+ demonspawn();
+ }
+
+ if (you.attribute[ATTR_NUM_DEMONIC_POWERS] == 2
+ && you.experience_level > 9
+ && (you.experience_level == 14
+ || (you.experience_level < 14 && one_chance_in(3))))
+ {
+ demonspawn();
+ }
+
+ if (you.attribute[ATTR_NUM_DEMONIC_POWERS] == 3
+ && you.experience_level > 14
+ && (you.experience_level == 19
+ || (you.experience_level < 19 && one_chance_in(3))))
+ {
+ demonspawn();
+ }
+
+ if (you.attribute[ATTR_NUM_DEMONIC_POWERS] == 4
+ && you.experience_level > 19
+ && (you.experience_level == 24
+ || (you.experience_level < 24 && one_chance_in(3))))
+ {
+ demonspawn();
+ }
+
+ if (you.attribute[ATTR_NUM_DEMONIC_POWERS] == 5
+ && you.experience_level == 27)
+ {
+ demonspawn();
+ }
+
+/*if (you.attribute [ATTR_NUM_DEMONIC_POWERS] == 6 && (you.experience_level == 8 || (you.experience_level < 8 && one_chance_in(3) ) )
+ demonspawn(); */
+ if (!(you.experience_level % 4))
+ modify_stat(STAT_RANDOM, 1, false);
+ break;
+
+ case SP_GHOUL:
+ // lowered because of HD raise -- bwr
+ // if (you.experience_level < 17)
+ // hp_adjust++;
+
+ if (!(you.experience_level % 2))
+ hp_adjust++;
+
+ if (!(you.experience_level % 3))
+ mp_adjust--;
+
+ if (!(you.experience_level % 5))
+ modify_stat(STAT_STRENGTH, 1, false);
+ break;
+
+ case SP_KENKU:
+ if (you.experience_level < 17)
+ hp_adjust--;
+
+ if (!(you.experience_level % 3))
+ hp_adjust--;
+
+ if (!(you.experience_level % 4))
+ modify_stat(STAT_RANDOM, 1, false);
+
+ if (you.experience_level == 5)
+ mpr("You have gained the ability to fly.", MSGCH_INTRINSIC_GAIN);
+ else if (you.experience_level == 15)
+ mpr("You can now fly continuously.", MSGCH_INTRINSIC_GAIN);
+ break;
+
+ case SP_MERFOLK:
+ if (you.experience_level % 3)
+ hp_adjust++;
+
+ if (!(you.experience_level % 5))
+ modify_stat(STAT_RANDOM, 1, false);
+ break;
+ }
+ }
+
+ // add hp and mp adjustments - GDL
+ inc_max_hp( hp_adjust );
+ inc_max_mp( mp_adjust );
+
+ deflate_hp( you.hp_max, false );
+
+ if (you.magic_points < 0)
+ you.magic_points = 0;
+
+ calc_hp();
+ calc_mp();
+
+ if (you.experience_level > you.max_level)
+ you.max_level = you.experience_level;
+
+ if (you.religion == GOD_XOM)
+ Xom_acts(true, you.experience_level, true);
+ }
+
+ redraw_skill( you.your_name, player_title() );
+} // end level_change()
+
+// here's a question for you: does the ordering of mods make a difference?
+// (yes) -- are these things in the right order of application to stealth?
+// - 12mar2000 {dlb}
+int check_stealth(void)
+{
+ if (you.special_wield == SPWLD_SHADOW)
+ return (0);
+
+ int stealth = you.dex * 3;
+
+ if (you.skills[SK_STEALTH])
+ {
+ if (player_genus(GENPC_DRACONIAN))
+ stealth += (you.skills[SK_STEALTH] * 12);
+ else
+ {
+ switch (you.species)
+ {
+ case SP_TROLL:
+ case SP_OGRE:
+ case SP_OGRE_MAGE:
+ case SP_CENTAUR:
+ stealth += (you.skills[SK_STEALTH] * 9);
+ break;
+ case SP_MINOTAUR:
+ stealth += (you.skills[SK_STEALTH] * 12);
+ break;
+ case SP_GNOME:
+ case SP_HALFLING:
+ case SP_KOBOLD:
+ case SP_SPRIGGAN:
+ case SP_NAGA: // not small but very good at stealth
+ stealth += (you.skills[SK_STEALTH] * 18);
+ break;
+ default:
+ stealth += (you.skills[SK_STEALTH] * 15);
+ break;
+ }
+ }
+ }
+
+ if (you.burden_state == BS_ENCUMBERED)
+ stealth /= 2;
+ else if (you.burden_state == BS_OVERLOADED)
+ stealth /= 5;
+
+ if (you.conf)
+ stealth /= 3;
+
+ const int arm = you.equip[EQ_BODY_ARMOUR];
+ const int cloak = you.equip[EQ_CLOAK];
+ const int boots = you.equip[EQ_BOOTS];
+
+ if (arm != -1 && !player_light_armour())
+ stealth -= (mass_item( you.inv[arm] ) / 10);
+
+ if (cloak != -1 && cmp_equip_race( you.inv[cloak], ISFLAG_ELVEN ))
+ stealth += 20;
+
+ if (boots != -1)
+ {
+ if (get_armour_ego_type( you.inv[boots] ) == SPARM_STEALTH)
+ stealth += 50;
+
+ if (cmp_equip_race( you.inv[boots], ISFLAG_ELVEN ))
+ stealth += 20;
+ }
+
+ stealth += scan_randarts( RAP_STEALTH );
+
+ if (player_is_levitating())
+ stealth += 10;
+ else if (player_in_water())
+ {
+ // Merfolk can sneak up on monsters underwater -- bwr
+ if (you.species == SP_MERFOLK)
+ stealth += 50;
+ else
+ stealth /= 2; // splashy-splashy
+ }
+
+ // Radiating silence is the negative compliment of shouting all the
+ // time... a sudden change from background noise to no noise is going
+ // to clue anything in to the fact that something is very wrong...
+ // a personal silence spell would naturally be different, but this
+ // silence radiates for a distance and prevents monster spellcasting,
+ // which pretty much gives away the stealth game.
+ if (you.duration[DUR_SILENCE])
+ stealth -= 50;
+
+ if (stealth < 0)
+ stealth = 0;
+
+ return (stealth);
+} // end check_stealth()
+
+void ability_increase(void)
+{
+ unsigned char keyin;
+
+ mpr("Your experience leads to an increase in your attributes!",
+ MSGCH_INTRINSIC_GAIN);
+
+ more();
+ mesclr();
+
+ mpr("Increase (S)trength, (I)ntelligence, or (D)exterity? ", MSGCH_PROMPT);
+
+ get_key:
+ keyin = getch();
+ if (keyin == 0)
+ {
+ getch();
+ goto get_key;
+ }
+
+ switch (keyin)
+ {
+ case 's':
+ case 'S':
+ modify_stat(STAT_STRENGTH, 1, false);
+ return;
+
+ case 'i':
+ case 'I':
+ modify_stat(STAT_INTELLIGENCE, 1, false);
+ return;
+
+ case 'd':
+ case 'D':
+ modify_stat(STAT_DEXTERITY, 1, false);
+ return;
+ }
+
+ goto get_key;
+/* this is an infinite loop because it is reasonable to assume that you're not going to want to leave it prematurely. */
+} // end ability_increase()
+
+void display_char_status(void)
+{
+ if (you.is_undead)
+ mpr( "You are undead." );
+ else if (you.deaths_door)
+ mpr( "You are standing in death's doorway." );
+ else
+ mpr( "You are alive." );
+
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_SPIDER:
+ mpr( "You are in spider-form." );
+ break;
+ case TRAN_BLADE_HANDS:
+ mpr( "You have blades for hands." );
+ break;
+ case TRAN_STATUE:
+ mpr( "You are a statue." );
+ break;
+ case TRAN_ICE_BEAST:
+ mpr( "You are an ice creature." );
+ break;
+ case TRAN_DRAGON:
+ mpr( "You are in dragon-form." );
+ break;
+ case TRAN_LICH:
+ mpr( "You are in lich-form." );
+ break;
+ case TRAN_SERPENT_OF_HELL:
+ mpr( "You are a huge demonic serpent." );
+ break;
+ case TRAN_AIR:
+ mpr( "You are a cloud of diffuse gas." );
+ break;
+ }
+
+ if (you.duration[DUR_BREATH_WEAPON])
+ mpr( "You are short of breath." );
+
+ if (you.duration[DUR_REPEL_UNDEAD])
+ mpr( "You have a holy aura protecting you from undead." );
+
+ if (you.duration[DUR_LIQUID_FLAMES])
+ mpr( "You are covered in liquid flames." );
+
+ if (you.duration[DUR_ICY_ARMOUR])
+ mpr( "You are protected by an icy shield." );
+
+ if (you.duration[DUR_REPEL_MISSILES])
+ mpr( "You are protected from missiles." );
+
+ if (you.duration[DUR_DEFLECT_MISSILES])
+ mpr( "You deflect missiles." );
+
+ if (you.duration[DUR_PRAYER])
+ mpr( "You are praying." );
+
+ if (you.duration[DUR_REGENERATION])
+ mpr( "You are regenerating." );
+
+ if (you.duration[DUR_SWIFTNESS])
+ mpr( "You can move swiftly." );
+
+ if (you.duration[DUR_INSULATION])
+ mpr( "You are insulated." );
+
+ if (you.duration[DUR_STONEMAIL])
+ mpr( "You are covered in scales of stone." );
+
+ if (you.duration[DUR_CONTROLLED_FLIGHT])
+ mpr( "You can control your flight." );
+
+ if (you.duration[DUR_TELEPORT])
+ mpr( "You are about to teleport." );
+
+ if (you.duration[DUR_CONTROL_TELEPORT])
+ mpr( "You can control teleportation." );
+
+ if (you.duration[DUR_DEATH_CHANNEL])
+ mpr( "You are channeling the dead." );
+
+ if (you.duration[DUR_FORESCRY]) //jmf: added 19mar2000
+ mpr( "You are forewarned." );
+
+ if (you.duration[DUR_SILENCE]) //jmf: added 27mar2000
+ mpr( "You radiate silence." );
+
+ if (you.duration[DUR_INFECTED_SHUGGOTH_SEED]) //jmf: added 19mar2000
+ mpr( "You are infected with a shuggoth parasite." );
+
+ if (you.duration[DUR_STONESKIN])
+ mpr( "Your skin is tough as stone." );
+
+ if (you.duration[DUR_SEE_INVISIBLE])
+ mpr( "You can see invisible." );
+
+ if (you.invis)
+ mpr( "You are invisible." );
+
+ if (you.conf)
+ mpr( "You are confused." );
+
+ if (you.paralysis)
+ mpr( "You are paralysed." );
+
+ if (you.exhausted)
+ mpr( "You are exhausted." );
+
+ if (you.slow && you.haste)
+ mpr( "You are under both slowing and hasting effects." );
+ else if (you.slow)
+ mpr( "You are moving very slowly." );
+ else if (you.haste)
+ mpr( "You are moving very quickly." );
+
+ if (you.might)
+ mpr( "You are mighty." );
+
+ if (you.berserker)
+ mpr( "You are possessed by a berserker rage." );
+
+ if (player_is_levitating())
+ mpr( "You are hovering above the floor." );
+
+ if (you.poison)
+ {
+ snprintf( info, INFO_SIZE, "You are %s poisoned.",
+ (you.poison > 10) ? "extremely" :
+ (you.poison > 5) ? "very" :
+ (you.poison > 3) ? "quite"
+ : "mildly" );
+ mpr(info);
+ }
+
+ if (you.disease)
+ {
+ snprintf( info, INFO_SIZE, "You are %sdiseased.",
+ (you.disease > 120) ? "badly " :
+ (you.disease > 40) ? ""
+ : "mildly " );
+ mpr(info);
+ }
+
+ if (you.rotting || you.species == SP_GHOUL)
+ {
+ // I apologize in advance for the horrendous ugliness about to
+ // transpire. Avert your eyes!
+ snprintf( info, INFO_SIZE, "Your flesh is rotting%s",
+ (you.rotting > 15) ? " before your eyes!":
+ (you.rotting > 8) ? " away quickly.":
+ (you.rotting > 4) ? " badly."
+ : ((you.species == SP_GHOUL && you.rotting > 0)
+ ? " faster than usual." : ".") );
+ mpr(info);
+ }
+
+ contaminate_player( 0, true );
+
+ if (you.confusing_touch)
+ {
+ snprintf( info, INFO_SIZE, "Your hands are glowing %s red.",
+ (you.confusing_touch > 40) ? "an extremely bright" :
+ (you.confusing_touch > 20) ? "bright"
+ : "a soft" );
+ mpr(info);
+ }
+
+ if (you.sure_blade)
+ {
+ snprintf( info, INFO_SIZE, "You have a %sbond with your blade.",
+ (you.sure_blade > 15) ? "strong " :
+ (you.sure_blade > 5) ? ""
+ : "weak " );
+ mpr(info);
+ }
+} // end display_char_status()
+
+void redraw_skill(const char your_name[kNameLen], const char class_name[80])
+{
+ char print_it[80];
+
+ memset( print_it, ' ', sizeof(print_it) );
+ snprintf( print_it, sizeof(print_it), "%s the %s", your_name, class_name );
+
+ int in_len = strlen( print_it );
+ if (in_len > 40)
+ {
+ in_len -= 3; // what we're getting back from removing "the"
+
+ const int name_len = strlen(your_name);
+ char name_buff[kNameLen];
+ strncpy( name_buff, your_name, kNameLen );
+
+ // squeeze name if required, the "- 8" is too not squueze too much
+ if (in_len > 40 && (name_len - 8) > (in_len - 40))
+ name_buff[ name_len - (in_len - 40) - 1 ] = '\0';
+ else
+ name_buff[ kNameLen - 1 ] = '\0';
+
+ snprintf( print_it, sizeof(print_it), "%s, %s", name_buff, class_name );
+ }
+
+ for (int i = strlen(print_it); i < 41; i++)
+ print_it[i] = ' ';
+
+ print_it[40] = '\0';
+
+#ifdef DOS_TERM
+ window(1, 1, 80, 25);
+#endif
+ gotoxy(40, 1);
+
+ textcolor( LIGHTGREY );
+ cprintf( print_it );
+} // end redraw_skill()
+
+// Note that this function only has the one static buffer, so if you
+// want to use the results, you'll want to make a copy.
+char *species_name( int speci, int level, bool genus, bool adj, bool cap )
+// defaults: false false true
+{
+ static char species_buff[80];
+
+ if (player_genus( GENPC_DRACONIAN, speci ))
+ {
+ if (adj || genus) // adj doesn't care about exact species
+ strcpy( species_buff, "Draconian" );
+ else
+ {
+ // No longer have problems with ghosts here -- Sharp Aug2002
+ if (level < 7)
+ strcpy( species_buff, "Draconian" );
+ else
+ {
+ switch (speci)
+ {
+ case SP_RED_DRACONIAN:
+ strcpy( species_buff, "Red Draconian" );
+ break;
+ case SP_WHITE_DRACONIAN:
+ strcpy( species_buff, "White Draconian" );
+ break;
+ case SP_GREEN_DRACONIAN:
+ strcpy( species_buff, "Green Draconian" );
+ break;
+ case SP_GOLDEN_DRACONIAN:
+ strcpy( species_buff, "Yellow Draconian" );
+ break;
+ case SP_GREY_DRACONIAN:
+ strcpy( species_buff, "Grey Draconian" );
+ break;
+ case SP_BLACK_DRACONIAN:
+ strcpy( species_buff, "Black Draconian" );
+ break;
+ case SP_PURPLE_DRACONIAN:
+ strcpy( species_buff, "Purple Draconian" );
+ break;
+ case SP_MOTTLED_DRACONIAN:
+ strcpy( species_buff, "Mottled Draconian" );
+ break;
+ case SP_PALE_DRACONIAN:
+ strcpy( species_buff, "Pale Draconian" );
+ break;
+ case SP_UNK0_DRACONIAN:
+ case SP_UNK1_DRACONIAN:
+ case SP_UNK2_DRACONIAN:
+ default:
+ strcpy( species_buff, "Draconian" );
+ break;
+ }
+ }
+ }
+ }
+ else if (player_genus( GENPC_ELVEN, speci ))
+ {
+ if (adj) // doesn't care about species/genus
+ strcpy( species_buff, "Elven" );
+ else if (genus)
+ strcpy( species_buff, "Elf" );
+ else
+ {
+ switch (speci)
+ {
+ case SP_ELF:
+ default:
+ strcpy( species_buff, "Elf" );
+ break;
+ case SP_HIGH_ELF:
+ strcpy( species_buff, "High Elf" );
+ break;
+ case SP_GREY_ELF:
+ strcpy( species_buff, "Grey Elf" );
+ break;
+ case SP_DEEP_ELF:
+ strcpy( species_buff, "Deep Elf" );
+ break;
+ case SP_SLUDGE_ELF:
+ strcpy( species_buff, "Sludge Elf" );
+ break;
+ }
+ }
+ }
+ else if (player_genus( GENPC_DWARVEN, speci ))
+ {
+ if (adj) // doesn't care about species/genus
+ strcpy( species_buff, "Dwarven" );
+ else if (genus)
+ strcpy( species_buff, "Dwarf" );
+ else
+ {
+ switch (speci)
+ {
+ case SP_HILL_DWARF:
+ strcpy( species_buff, "Hill Dwarf" );
+ break;
+ case SP_MOUNTAIN_DWARF:
+ strcpy( species_buff, "Mountain Dwarf" );
+ break;
+ default:
+ strcpy( species_buff, "Dwarf" );
+ break;
+ }
+ }
+ }
+ else
+ {
+ switch (speci)
+ {
+ case SP_HUMAN:
+ strcpy( species_buff, "Human" );
+ break;
+ case SP_HALFLING:
+ strcpy( species_buff, "Halfling" );
+ break;
+ case SP_HILL_ORC:
+ strcpy( species_buff, (adj) ? "Orcish" : (genus) ? "Orc"
+ : "Hill Orc" );
+ break;
+ case SP_KOBOLD:
+ strcpy( species_buff, "Kobold" );
+ break;
+ case SP_MUMMY:
+ strcpy( species_buff, "Mummy" );
+ break;
+ case SP_NAGA:
+ strcpy( species_buff, "Naga" );
+ break;
+ case SP_GNOME:
+ strcpy( species_buff, (adj) ? "Gnomish" : "Gnome" );
+ break;
+ case SP_OGRE:
+ strcpy( species_buff, (adj) ? "Ogreish" : "Ogre" );
+ break;
+ case SP_TROLL:
+ strcpy( species_buff, (adj) ? "Trollish" : "Troll" );
+ break;
+ case SP_OGRE_MAGE:
+ // We've previously declared that these are radically
+ // different from Ogres... so we're not going to
+ // refer to them as Ogres. -- bwr
+ strcpy( species_buff, "Ogre-Mage" );
+ break;
+ case SP_CENTAUR:
+ strcpy( species_buff, "Centaur" );
+ break;
+ case SP_DEMIGOD:
+ strcpy( species_buff, (adj) ? "Divine" : "Demigod" );
+ break;
+ case SP_SPRIGGAN:
+ strcpy( species_buff, "Spriggan" );
+ break;
+ case SP_MINOTAUR:
+ strcpy( species_buff, "Minotaur" );
+ break;
+ case SP_DEMONSPAWN:
+ strcpy( species_buff, (adj) ? "Demonic" : "Demonspawn" );
+ break;
+ case SP_GHOUL:
+ strcpy( species_buff, (adj) ? "Ghoulish" : "Ghoul" );
+ break;
+ case SP_KENKU:
+ strcpy( species_buff, "Kenku" );
+ break;
+ case SP_MERFOLK:
+ strcpy( species_buff, (adj) ? "Merfolkian" : "Merfolk" );
+ break;
+ default:
+ strcpy( species_buff, (adj) ? "Yakish" : "Yak" );
+ break;
+ }
+ }
+
+ if (!cap)
+ strlwr( species_buff );
+
+ return (species_buff);
+} // end species_name()
+
+bool wearing_amulet(char amulet)
+{
+ if (amulet == AMU_CONTROLLED_FLIGHT
+ && (you.duration[DUR_CONTROLLED_FLIGHT]
+ || player_genus(GENPC_DRACONIAN)
+ || you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON))
+ {
+ return true;
+ }
+
+ if (amulet == AMU_CLARITY && you.mutation[MUT_CLARITY])
+ return true;
+
+ if (amulet == AMU_RESIST_CORROSION || amulet == AMU_CONSERVATION)
+ {
+ // this is hackish {dlb}
+ if (player_equip_ego_type( EQ_CLOAK, SPARM_PRESERVATION ))
+ return true;
+ }
+
+ if (you.equip[EQ_AMULET] == -1)
+ return false;
+
+ if (you.inv[you.equip[EQ_AMULET]].sub_type == amulet)
+ return true;
+
+ return false;
+} // end wearing_amulet()
+
+bool player_is_levitating(void)
+{
+ return (you.attribute[ATTR_TRANSFORMATION] == TRAN_DRAGON || you.levitation);
+}
+
+bool player_has_spell( int spell )
+{
+ for (int i = 0; i < 25; i++)
+ {
+ if (you.spells[i] == spell)
+ return (true);
+ }
+
+ return (false);
+}
+
+int species_exp_mod(char species)
+{
+
+ if (player_genus(GENPC_DRACONIAN))
+ return 14;
+ else if (player_genus(GENPC_DWARVEN))
+ return 13;
+ {
+ switch (species)
+ {
+ case SP_HUMAN:
+ case SP_HALFLING:
+ case SP_HILL_ORC:
+ case SP_KOBOLD:
+ return 10;
+ case SP_GNOME:
+ return 11;
+ case SP_ELF:
+ case SP_SLUDGE_ELF:
+ case SP_NAGA:
+ case SP_GHOUL:
+ case SP_MERFOLK:
+ return 12;
+ case SP_SPRIGGAN:
+ case SP_KENKU:
+ return 13;
+ case SP_GREY_ELF:
+ case SP_DEEP_ELF:
+ case SP_OGRE:
+ case SP_CENTAUR:
+ case SP_MINOTAUR:
+ case SP_DEMONSPAWN:
+ return 14;
+ case SP_HIGH_ELF:
+ case SP_MUMMY:
+ case SP_TROLL:
+ case SP_OGRE_MAGE:
+ return 15;
+ case SP_DEMIGOD:
+ return 16;
+ default:
+ return 0;
+ }
+ }
+} // end species_exp_mod()
+
+unsigned long exp_needed(int lev)
+{
+ lev--;
+
+ unsigned long level = 0;
+
+#if 0
+ case 1: level = 1;
+ case 2: level = 10;
+ case 3: level = 35;
+ case 4: level = 70;
+ case 5: level = 120;
+ case 6: level = 250;
+ case 7: level = 510;
+ case 8: level = 900;
+ case 9: level = 1700;
+ case 10: level = 3500;
+ case 11: level = 8000;
+ case 12: level = 20000;
+
+ default: //return 14000 * (lev - 11);
+ level = 20000 * (lev - 11) + ((lev - 11) * (lev - 11) * (lev - 11)) * 130;
+ break;
+#endif
+
+ // This is a better behaved function than the above. The above looks
+ // really ugly when you consider the second derivative, its not smooth
+ // and has a horrible bump at level 12 followed by comparitively easy
+ // teen levels. This tries to sort out those issues.
+ //
+ // Basic plan:
+ // Section 1: levels 1- 5, second derivative goes 10-10-20-30.
+ // Section 2: levels 6-13, second derivative is exponential/doubling.
+ // Section 3: levels 14-27, second derivative is constant at 6000.
+ //
+ // Section three is constant so we end up with high levels at about
+ // their old values (level 27 at 850k), without delta2 ever decreasing.
+ // The values that are considerably different (ie level 13 is now 29000,
+ // down from 41040 are because the second derivative goes from 9040 to
+ // 1430 at that point in the original, and then slowly builds back
+ // up again). This function smoothes out the old level 10-15 area
+ // considerably.
+
+ // Here's a table:
+ //
+ // level xp delta delta2
+ // ===== ======= ===== ======
+ // 1 0 0 0
+ // 2 10 10 10
+ // 3 30 20 10
+ // 4 70 40 20
+ // 5 140 70 30
+ // 6 270 130 60
+ // 7 520 250 120
+ // 8 1010 490 240
+ // 9 1980 970 480
+ // 10 3910 1930 960
+ // 11 7760 3850 1920
+ // 12 15450 7690 3840
+ // 13 29000 13550 5860
+ // 14 48500 19500 5950
+ // 15 74000 25500 6000
+ // 16 105500 31500 6000
+ // 17 143000 37500 6000
+ // 18 186500 43500 6000
+ // 19 236000 49500 6000
+ // 20 291500 55500 6000
+ // 21 353000 61500 6000
+ // 22 420500 67500 6000
+ // 23 494000 73500 6000
+ // 24 573500 79500 6000
+ // 25 659000 85500 6000
+ // 26 750500 91500 6000
+ // 27 848000 97500 6000
+
+
+ switch (lev)
+ {
+ case 1:
+ level = 1;
+ break;
+ case 2:
+ level = 10;
+ break;
+ case 3:
+ level = 30;
+ break;
+ case 4:
+ level = 70;
+ break;
+
+ default:
+ if (lev < 13)
+ {
+ lev -= 4;
+ level = 10 + 10 * lev
+ + 30 * (static_cast<int>(pow( 2.0, lev + 1 )));
+ }
+ else
+ {
+ lev -= 12;
+ level = 15500 + 10500 * lev + 3000 * lev * lev;
+ }
+ break;
+ }
+
+ return ((level - 1) * species_exp_mod(you.species) / 10);
+} // end exp_needed()
+
+// returns bonuses from rings of slaying, etc.
+int slaying_bonus(char which_affected)
+{
+ int ret = 0;
+
+ if (which_affected == PWPN_HIT)
+ {
+ ret += player_equip( EQ_RINGS_PLUS, RING_SLAYING );
+ ret += scan_randarts(RAP_ACCURACY);
+ }
+ else if (which_affected == PWPN_DAMAGE)
+ {
+ ret += player_equip( EQ_RINGS_PLUS2, RING_SLAYING );
+ ret += scan_randarts(RAP_DAMAGE);
+ }
+
+ return (ret);
+} // end slaying_bonus()
+
+/* Checks each equip slot for a randart, and adds up all of those with
+ a given property. Slow if any randarts are worn, so avoid where possible. */
+int scan_randarts(char which_property)
+{
+ int i = 0;
+ int retval = 0;
+
+ for (i = EQ_WEAPON; i < NUM_EQUIP; i++)
+ {
+ const int eq = you.equip[i];
+
+ if (eq == -1)
+ continue;
+
+ // only weapons give their effects when in our hands
+ if (i == EQ_WEAPON && you.inv[ eq ].base_type != OBJ_WEAPONS)
+ continue;
+
+ if (!is_random_artefact( you.inv[ eq ] ))
+ continue;
+
+ retval += randart_wpn_property( you.inv[ eq ], which_property );
+ }
+
+ return (retval);
+} // end scan_randarts()
+
+void modify_stat(unsigned char which_stat, char amount, bool suppress_msg)
+{
+ char *ptr_stat = NULL;
+ char *ptr_stat_max = NULL;
+ char *ptr_redraw = NULL;
+
+ // sanity - is non-zero amount?
+ if (amount == 0)
+ return;
+
+ if (!suppress_msg)
+ strcpy(info, "You feel ");
+
+ if (which_stat == STAT_RANDOM)
+ which_stat = random2(NUM_STATS);
+
+ switch (which_stat)
+ {
+ case STAT_STRENGTH:
+ ptr_stat = &you.strength;
+ ptr_stat_max = &you.max_strength;
+ ptr_redraw = &you.redraw_strength;
+ if (!suppress_msg)
+ strcat(info, (amount > 0) ? "stronger." : "weaker.");
+ break;
+
+ case STAT_DEXTERITY:
+ ptr_stat = &you.dex;
+ ptr_stat_max = &you.max_dex;
+ ptr_redraw = &you.redraw_dexterity;
+ if (!suppress_msg)
+ strcat(info, (amount > 0) ? "agile." : "clumsy.");
+ break;
+
+ case STAT_INTELLIGENCE:
+ ptr_stat = &you.intel;
+ ptr_stat_max = &you.max_intel;
+ ptr_redraw = &you.redraw_intelligence;
+ if (!suppress_msg)
+ strcat(info, (amount > 0) ? "clever." : "stupid.");
+ break;
+ }
+
+ if (!suppress_msg)
+ mpr( info, (amount > 0) ? MSGCH_INTRINSIC_GAIN : MSGCH_WARN );
+
+ *ptr_stat += amount;
+ *ptr_stat_max += amount;
+ *ptr_redraw = 1;
+
+ if (ptr_stat == &you.strength)
+ burden_change();
+
+ return;
+} // end modify_stat()
+
+void dec_hp(int hp_loss, bool fatal)
+{
+ if (hp_loss < 1)
+ return;
+
+ you.hp -= hp_loss;
+
+ if (!fatal && you.hp < 1)
+ you.hp = 1;
+
+ you.redraw_hit_points = 1;
+
+ return;
+} // end dec_hp()
+
+void dec_mp(int mp_loss)
+{
+ if (mp_loss < 1)
+ return;
+
+ you.magic_points -= mp_loss;
+
+ if (you.magic_points < 0)
+ you.magic_points = 0;
+
+ you.redraw_magic_points = 1;
+
+ return;
+} // end dec_mp()
+
+bool enough_hp(int minimum, bool suppress_msg)
+{
+ // We want to at least keep 1 HP -- bwr
+ if (you.hp < minimum + 1)
+ {
+ if (!suppress_msg)
+ mpr("You haven't enough vitality at the moment.");
+
+ return false;
+ }
+
+ return true;
+} // end enough_hp()
+
+bool enough_mp(int minimum, bool suppress_msg)
+{
+ if (you.magic_points < minimum)
+ {
+ if (!suppress_msg)
+ mpr("You haven't enough magic at the moment.");
+
+ return false;
+ }
+
+ return true;
+} // end enough_mp()
+
+// Note that "max_too" refers to the base potential, the actual
+// resulting max value is subject to penalties, bonuses, and scalings.
+void inc_mp(int mp_gain, bool max_too)
+{
+ if (mp_gain < 1)
+ return;
+
+ you.magic_points += mp_gain;
+
+ if (max_too)
+ inc_max_mp( mp_gain );
+
+ if (you.magic_points > you.max_magic_points)
+ you.magic_points = you.max_magic_points;
+
+ you.redraw_magic_points = 1;
+
+ return;
+} // end inc_mp()
+
+// Note that "max_too" refers to the base potential, the actual
+// resulting max value is subject to penalties, bonuses, and scalings.
+void inc_hp(int hp_gain, bool max_too)
+{
+ if (hp_gain < 1)
+ return;
+
+ you.hp += hp_gain;
+
+ if (max_too)
+ inc_max_hp( hp_gain );
+
+ if (you.hp > you.hp_max)
+ you.hp = you.hp_max;
+
+ you.redraw_hit_points = 1;
+} // end inc_hp()
+
+void rot_hp( int hp_loss )
+{
+ you.base_hp -= hp_loss;
+ calc_hp();
+
+ you.redraw_hit_points = 1;
+}
+
+void unrot_hp( int hp_recovered )
+{
+ if (hp_recovered >= 5000 - you.base_hp)
+ you.base_hp = 5000;
+ else
+ you.base_hp += hp_recovered;
+
+ calc_hp();
+
+ you.redraw_hit_points = 1;
+}
+
+int player_rotted( void )
+{
+ return (5000 - you.base_hp);
+}
+
+void rot_mp( int mp_loss )
+{
+ you.base_magic_points -= mp_loss;
+ calc_mp();
+
+ you.redraw_magic_points = 1;
+}
+
+void inc_max_hp( int hp_gain )
+{
+ you.base_hp2 += hp_gain;
+ calc_hp();
+
+ you.redraw_hit_points = 1;
+}
+
+void dec_max_hp( int hp_loss )
+{
+ you.base_hp2 -= hp_loss;
+ calc_hp();
+
+ you.redraw_hit_points = 1;
+}
+
+void inc_max_mp( int mp_gain )
+{
+ you.base_magic_points2 += mp_gain;
+ calc_mp();
+
+ you.redraw_magic_points = 1;
+}
+
+void dec_max_mp( int mp_loss )
+{
+ you.base_magic_points2 -= mp_loss;
+ calc_mp();
+
+ you.redraw_magic_points = 1;
+}
+
+// use of floor: false = hp max, true = hp min {dlb}
+void deflate_hp(int new_level, bool floor)
+{
+ if (floor && you.hp < new_level)
+ you.hp = new_level;
+ else if (!floor && you.hp > new_level)
+ you.hp = new_level;
+
+ // must remain outside conditional, given code usage {dlb}
+ you.redraw_hit_points = 1;
+
+ return;
+} // end deflate_hp()
+
+// Note that "max_too" refers to the base potential, the actual
+// resulting max value is subject to penalties, bonuses, and scalings.
+void set_hp(int new_amount, bool max_too)
+{
+ if (you.hp != new_amount)
+ you.hp = new_amount;
+
+ if (max_too && you.hp_max != new_amount)
+ {
+ you.base_hp2 = 5000 + new_amount;
+ calc_hp();
+ }
+
+ if (you.hp > you.hp_max)
+ you.hp = you.hp_max;
+
+ // must remain outside conditional, given code usage {dlb}
+ you.redraw_hit_points = 1;
+
+ return;
+} // end set_hp()
+
+// Note that "max_too" refers to the base potential, the actual
+// resulting max value is subject to penalties, bonuses, and scalings.
+void set_mp(int new_amount, bool max_too)
+{
+ if (you.magic_points != new_amount)
+ you.magic_points = new_amount;
+
+ if (max_too && you.max_magic_points != new_amount)
+ {
+ // note that this gets scaled down for values > 18
+ you.base_magic_points2 = 5000 + new_amount;
+ calc_mp();
+ }
+
+ if (you.magic_points > you.max_magic_points)
+ you.magic_points = you.max_magic_points;
+
+ // must remain outside conditional, given code usage {dlb}
+ you.redraw_magic_points = 1;
+
+ return;
+} // end set_mp()
+
+
+static const char * Species_Abbrev_List[ NUM_SPECIES ] =
+ { "XX", "Hu", "El", "HE", "GE", "DE", "SE", "HD", "MD", "Ha",
+ "HO", "Ko", "Mu", "Na", "Gn", "Og", "Tr", "OM", "Dr", "Dr",
+ "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr", "Dr",
+ "Ce", "DG", "Sp", "Mi", "DS", "Gh", "Ke", "Mf" };
+
+int get_species_index_by_abbrev( const char *abbrev )
+{
+ int i;
+ for (i = SP_HUMAN; i < NUM_SPECIES; i++)
+ {
+ if (tolower( abbrev[0] ) == tolower( Species_Abbrev_List[i][0] )
+ && tolower( abbrev[1] ) == tolower( Species_Abbrev_List[i][1] ))
+ {
+ break;
+ }
+ }
+
+ return ((i < NUM_SPECIES) ? i : -1);
+}
+
+int get_species_index_by_name( const char *name )
+{
+ int i;
+ int sp = -1;
+
+ char *ptr;
+ char lowered_buff[80];
+
+ strncpy( lowered_buff, name, sizeof( lowered_buff ) );
+ strlwr( lowered_buff );
+
+ for (i = SP_HUMAN; i < NUM_SPECIES; i++)
+ {
+ char *lowered_species = species_name( i, 0, false, false, false );
+ ptr = strstr( lowered_species, lowered_buff );
+ if (ptr != NULL)
+ {
+ sp = i;
+ if (ptr == lowered_species) // prefix takes preference
+ break;
+ }
+ }
+
+ return (sp);
+}
+
+const char *get_species_abbrev( int which_species )
+{
+ ASSERT( which_species > 0 && which_species < NUM_SPECIES );
+
+ return (Species_Abbrev_List[ which_species ]);
+}
+
+
+static const char * Class_Abbrev_List[ NUM_JOBS ] =
+ { "Fi", "Wz", "Pr", "Th", "Gl", "Ne", "Pa", "As", "Be", "Hu",
+ "Cj", "En", "FE", "IE", "Su", "AE", "EE", "Cr", "DK", "VM",
+ "CK", "Tm", "He", "XX", "Re", "St", "Mo", "Wr", "Wn" };
+
+static const char * Class_Name_List[ NUM_JOBS ] =
+ { "Fighter", "Wizard", "Priest", "Thief", "Gladiator", "Necromancer",
+ "Paladin", "Assassin", "Berserker", "Hunter", "Conjurer", "Enchanter",
+ "Fire Elementalist", "Ice Elementalist", "Summoner", "Air Elementalist",
+ "Earth Elementalist", "Crusader", "Death Knight", "Venom Mage",
+ "Chaos Knight", "Transmuter", "Healer", "Quitter", "Reaver", "Stalker",
+ "Monk", "Warper", "Wanderer" };
+
+int get_class_index_by_abbrev( const char *abbrev )
+{
+ int i;
+
+ for (i = 0; i < NUM_JOBS; i++)
+ {
+ if (i == JOB_QUITTER)
+ continue;
+
+ if (tolower( abbrev[0] ) == tolower( Class_Abbrev_List[i][0] )
+ && tolower( abbrev[1] ) == tolower( Class_Abbrev_List[i][1] ))
+ {
+ break;
+ }
+ }
+
+ return ((i < NUM_JOBS) ? i : -1);
+}
+
+const char *get_class_abbrev( int which_job )
+{
+ ASSERT( which_job < NUM_JOBS && which_job != JOB_QUITTER );
+
+ return (Class_Abbrev_List[ which_job ]);
+}
+
+int get_class_index_by_name( const char *name )
+{
+ int i;
+ int cl = -1;
+
+ char *ptr;
+ char lowered_buff[80];
+ char lowered_class[80];
+
+ strncpy( lowered_buff, name, sizeof( lowered_buff ) );
+ strlwr( lowered_buff );
+
+ for (i = 0; i < NUM_JOBS; i++)
+ {
+ if (i == JOB_QUITTER)
+ continue;
+
+ strncpy( lowered_class, Class_Name_List[i], sizeof( lowered_class ) );
+ strlwr( lowered_class );
+
+ ptr = strstr( lowered_class, lowered_buff );
+ if (ptr != NULL)
+ {
+ cl = i;
+ if (ptr == lowered_class) // prefix takes preference
+ break;
+ }
+ }
+
+ return (cl);
+}
+
+const char *get_class_name( int which_job )
+{
+ ASSERT( which_job < NUM_JOBS && which_job != JOB_QUITTER );
+
+ return (Class_Name_List[ which_job ]);
+}
+
+/* ******************************************************************
+
+// this function is solely called by a commented out portion of
+// player::level_change() and is a bit outta whack with the
+// current codebase - probably should be struck as well 19may2000 {dlb}
+
+void priest_spells( int priest_pass[10], char religious )
+{
+
+ switch ( religious )
+ {
+ case GOD_ZIN:
+ priest_pass[1] = SPELL_LESSER_HEALING;
+ priest_pass[2] = SPELL_REPEL_UNDEAD;
+ priest_pass[3] = SPELL_HEAL_OTHER;
+ priest_pass[4] = SPELL_PURIFICATION;
+ priest_pass[5] = SPELL_GREATER_HEALING;
+ priest_pass[6] = SPELL_SMITING;
+ priest_pass[7] = SPELL_HOLY_WORD;
+ priest_pass[8] = SPELL_REMOVE_CURSE;
+ priest_pass[9] = SPELL_GUARDIAN;
+ break;
+
+ case GOD_SHINING_ONE:
+ priest_pass[1] = SPELL_REPEL_UNDEAD;
+ priest_pass[2] = SPELL_LESSER_HEALING;
+ priest_pass[3] = SPELL_HEAL_OTHER;
+ priest_pass[4] = SPELL_PURIFICATION;
+ priest_pass[5] = SPELL_ABJURATION_II;
+ priest_pass[6] = SPELL_THUNDERBOLT;
+ priest_pass[7] = SPELL_SHINING_LIGHT;
+ priest_pass[8] = SPELL_SUMMON_DAEVA;
+ priest_pass[9] = SPELL_FLAME_OF_CLEANSING;
+ break;
+
+ case GOD_ELYVILON:
+ priest_pass[1] = SPELL_LESSER_HEALING;
+ priest_pass[2] = SPELL_HEAL_OTHER;
+ priest_pass[3] = SPELL_PURIFICATION;
+ priest_pass[4] = 93; // restore abilities
+ priest_pass[5] = SPELL_GREATER_HEALING;
+ priest_pass[6] = 94; // another healing spell
+ priest_pass[7] = 95; // something else
+ priest_pass[8] = -1; //
+ priest_pass[9] = -1; //
+ break;
+ }
+
+}
+
+// Spells to be added: (+ renamed!)
+// holy berserker
+// 87 Pestilence
+// 93 Restore Abilities
+// 94 something else healing
+// 95 something else
+
+****************************************************************** */
+
+void contaminate_player(int change, bool statusOnly)
+{
+ // get current contamination level
+ int old_level;
+ int new_level;
+
+
+#if DEBUG_DIAGNOSTICS
+ if (change > 0 || (change < 0 && you.magic_contamination))
+ {
+ snprintf( info, INFO_SIZE, "change: %d radiation: %d",
+ change, change + you.magic_contamination );
+
+ mpr( info, MSGCH_DIAGNOSTICS );
+ }
+#endif
+
+ old_level = (you.magic_contamination > 60)?(you.magic_contamination / 20 + 2) :
+ (you.magic_contamination > 40)?4 :
+ (you.magic_contamination > 25)?3 :
+ (you.magic_contamination > 15)?2 :
+ (you.magic_contamination > 5)?1 : 0;
+
+ // make the change
+ if (change + you.magic_contamination < 0)
+ you.magic_contamination = 0;
+ else
+ {
+ if (change + you.magic_contamination > 250)
+ you.magic_contamination = 250;
+ else
+ you.magic_contamination += change;
+ }
+
+ // figure out new level
+ new_level = (you.magic_contamination > 60)?(you.magic_contamination / 20 + 2) :
+ (you.magic_contamination > 40)?4 :
+ (you.magic_contamination > 25)?3 :
+ (you.magic_contamination > 15)?2 :
+ (you.magic_contamination > 5)?1 : 0;
+
+ if (statusOnly)
+ {
+ if (new_level > 0)
+ {
+ if (new_level > 3)
+ {
+ strcpy(info, (new_level == 4) ?
+ "Your entire body has taken on an eerie glow!" :
+ "You are engulfed in a nimbus of crackling magics!");
+ }
+ else
+ {
+ snprintf( info, INFO_SIZE, "You are %s with residual magics%c",
+ (new_level == 3) ? "practically glowing" :
+ (new_level == 2) ? "heavily infused"
+ : "contaminated",
+ (new_level == 3) ? '!' : '.');
+ }
+
+ mpr(info);
+ }
+ return;
+ }
+
+ if (new_level == old_level)
+ return;
+
+ snprintf( info, INFO_SIZE, "You feel %s contaminated with magical energies.",
+ (change < 0) ? "less" : "more" );
+
+ mpr( info, (change > 0) ? MSGCH_WARN : MSGCH_RECOVERY );
+}
+
+void poison_player( int amount, bool force )
+{
+ if ((!force && player_res_poison()) || amount <= 0)
+ return;
+
+ const int old_value = you.poison;
+ you.poison += amount;
+
+ if (you.poison > 40)
+ you.poison = 40;
+
+ if (you.poison > old_value)
+ {
+ snprintf( info, INFO_SIZE, "You are %spoisoned.",
+ (old_value > 0) ? "more " : "" );
+
+ // XXX: which message channel for this message?
+ mpr( info );
+ }
+}
+
+void reduce_poison_player( int amount )
+{
+ if (you.poison == 0 || amount <= 0)
+ return;
+
+ you.poison -= amount;
+
+ if (you.poison <= 0)
+ {
+ you.poison = 0;
+ mpr( "You feel better.", MSGCH_RECOVERY );
+ }
+ else
+ {
+ mpr( "You feel a little better.", MSGCH_RECOVERY );
+ }
+}
+
+void confuse_player( int amount, bool resistable )
+{
+ if (amount <= 0)
+ return;
+
+ if (resistable && wearing_amulet(AMU_CLARITY))
+ {
+ mpr( "You feel momentarily confused." );
+ return;
+ }
+
+ const int old_value = you.conf;
+ you.conf += amount;
+
+ if (you.conf > 40)
+ you.conf = 40;
+
+ if (you.conf > old_value)
+ {
+ snprintf( info, INFO_SIZE, "You are %sconfused.",
+ (old_value > 0) ? "more " : "" );
+
+ // XXX: which message channel for this message?
+ mpr( info );
+ }
+}
+
+void reduce_confuse_player( int amount )
+{
+ if (you.conf == 0 || amount <= 0)
+ return;
+
+ you.conf -= amount;
+
+ if (you.conf <= 0)
+ {
+ you.conf = 0;
+ mpr( "You feel less confused." );
+ }
+}
+
+void slow_player( int amount )
+{
+ if (amount <= 0)
+ return;
+
+ if (wearing_amulet( AMU_RESIST_SLOW ))
+ mpr("You feel momentarily lethargic.");
+ else if (you.slow >= 100)
+ mpr( "You already are as slow as you could be." );
+ else
+ {
+ if (you.slow == 0)
+ mpr( "You feel yourself slow down." );
+ else
+ mpr( "You feel as though you will be slow longer." );
+
+ you.slow += amount;
+
+ if (you.slow > 100)
+ you.slow = 100;
+ }
+}
+
+void dec_slow_player( void )
+{
+ if (you.slow > 1)
+ {
+ // BCR - Amulet of resist slow affects slow counter
+ if (wearing_amulet(AMU_RESIST_SLOW))
+ {
+ you.slow -= 5;
+ if (you.slow < 1)
+ you.slow = 1;
+ }
+ else
+ you.slow--;
+ }
+ else if (you.slow == 1)
+ {
+ mpr("You feel yourself speed up.", MSGCH_DURATION);
+ you.slow = 0;
+ }
+}
+
+void haste_player( int amount )
+{
+ bool amu_eff = wearing_amulet( AMU_RESIST_SLOW );
+
+ if (amount <= 0)
+ return;
+
+ if (amu_eff)
+ mpr( "Your amulet glows brightly." );
+
+ if (you.haste == 0)
+ mpr( "You feel yourself speed up." );
+ else if (you.haste > 80 + 20 * amu_eff)
+ mpr( "You already have as much speed as you can handle." );
+ else
+ {
+ mpr( "You feel as though your hastened speed will last longer." );
+ contaminate_player(1);
+ }
+
+ you.haste += amount;
+
+ if (you.haste > 80 + 20 * amu_eff)
+ you.haste = 80 + 20 * amu_eff;
+
+ naughty( NAUGHTY_STIMULANTS, 4 + random2(4) );
+}
+
+void dec_haste_player( void )
+{
+ if (you.haste > 1)
+ {
+ // BCR - Amulet of resist slow affects haste counter
+ if (!wearing_amulet(AMU_RESIST_SLOW) || coinflip())
+ you.haste--;
+
+ if (you.haste == 6)
+ {
+ mpr( "Your extra speed is starting to run out.", MSGCH_DURATION );
+ if (coinflip())
+ you.haste--;
+ }
+ }
+ else if (you.haste == 1)
+ {
+ mpr( "You feel yourself slow down.", MSGCH_DURATION );
+ you.haste = 0;
+ }
+}
+
+void disease_player( int amount )
+{
+ if (you.is_undead || amount <= 0)
+ return;
+
+ mpr( "You feel ill." );
+
+ const int tmp = you.disease + amount;
+ you.disease = (tmp > 210) ? 210 : tmp;
+}
+
+void dec_disease_player( void )
+{
+ if (you.disease > 0)
+ {
+ you.disease--;
+
+ if (you.disease > 5
+ && (you.species == SP_KOBOLD
+ || you.duration[ DUR_REGENERATION ]
+ || you.mutation[ MUT_REGENERATION ] == 3))
+ {
+ you.disease -= 2;
+ }
+
+ if (!you.disease)
+ mpr("You feel your health improve.", MSGCH_RECOVERY);
+ }
+}
+
+void rot_player( int amount )
+{
+ if (amount <= 0)
+ return;
+
+ if (you.rotting < 40)
+ {
+ // Either this, or the actual rotting message should probably
+ // be changed so that they're easier to tell apart. -- bwr
+ snprintf( info, INFO_SIZE, "You feel your flesh %s away!",
+ (you.rotting) ? "rotting" : "start to rot" );
+ mpr( info, MSGCH_WARN );
+
+ you.rotting += amount;
+ }
+}