summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2008-05-27 12:46:08 +0000
committerj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2008-05-27 12:46:08 +0000
commita86d2a52392ed50ba63ed9712c05f3574e45d695 (patch)
tree119721aaeb398f83686f783efcbf92ba5f3cd5f9
parent612b5c431864991b19105694131ba1314012e631 (diff)
downloadcrawl-ref-a86d2a52392ed50ba63ed9712c05f3574e45d695.tar.gz
crawl-ref-a86d2a52392ed50ba63ed9712c05f3574e45d695.zip
Fix 1974137: Stat-lowering transformations resulting in death by stat
loss. Now take to-be-removed equipment in account and don't transform in the removed equipment or the stat loss or both in combination would decrease one of your stats to (below) 0. For the vampire bat transformation, this check only happens after the ability failure check, so this could be improved. In any case, it's better than dying. Also, fix wrongly treating armour as worn when checking for stat loss by wearing. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@5294 c06c8d41-db1a-0410-9941-cceddc491573
-rw-r--r--crawl-ref/source/abl-show.cc27
-rw-r--r--crawl-ref/source/acr.cc16
-rw-r--r--crawl-ref/source/item_use.cc15
-rw-r--r--crawl-ref/source/transfor.cc197
-rw-r--r--crawl-ref/source/transfor.h3
5 files changed, 193 insertions, 65 deletions
diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc
index 7696b5589a..62be13def5 100644
--- a/crawl-ref/source/abl-show.cc
+++ b/crawl-ref/source/abl-show.cc
@@ -832,7 +832,7 @@ bool activate_ability()
return false;
}
- if ( you.duration[DUR_CONF] )
+ if (you.duration[DUR_CONF])
{
talents = your_talents(true);
if ( talents.empty() )
@@ -844,7 +844,7 @@ bool activate_ability()
}
int selected = -1;
- while ( selected < 0 )
+ while (selected < 0)
{
msg::streams(MSGCH_PROMPT) << "Use which ability? (? or * to list)"
<< std::endl;
@@ -871,7 +871,7 @@ bool activate_ability()
// try to find the hotkey
for (unsigned int i = 0; i < talents.size(); ++i)
{
- if ( talents[i].hotkey == keyin )
+ if (talents[i].hotkey == keyin)
{
selected = static_cast<int>(i);
break;
@@ -879,7 +879,7 @@ bool activate_ability()
}
// if we can't, cancel out
- if ( selected < 0 )
+ if (selected < 0)
{
mpr("You can't do that.");
crawl_state.zero_turns_taken();
@@ -894,17 +894,20 @@ bool activate_ability()
static bool _activate_talent(const talent& tal)
{
// Doing these would outright kill the player due to stat drain.
- if (tal.which == ABIL_TRAN_BAT && you.strength <= 5)
+ if (tal.which == ABIL_TRAN_BAT)
{
- mpr("You lack the strength for this transformation.");
- crawl_state.zero_turns_taken();
- return (false);
+ if (you.strength <= 5)
+ {
+ mpr("You lack the strength for this transformation.", MSGCH_WARN);
+ crawl_state.zero_turns_taken();
+ return (false);
+ }
}
else if (tal.which == ABIL_END_TRANSFORMATION
&& you.attribute[ATTR_TRANSFORMATION] == TRAN_BAT
&& you.dex <= 5)
{
- mpr("Turning back with such low dexterity would be fatal!");
+ mpr("Turning back with such low dexterity would be fatal!", MSGCH_WARN);
more();
crawl_state.zero_turns_taken();
return (false);
@@ -1831,7 +1834,11 @@ static bool _do_ability(const ability_def& abil)
break;
case ABIL_TRAN_BAT:
- transform(100, TRAN_BAT);
+ if (!transform(100, TRAN_BAT))
+ {
+ crawl_state.zero_turns_taken();
+ return (false);
+ }
break;
case ABIL_RENOUNCE_RELIGION:
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index 119e0adf5c..5b364affc1 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -3467,14 +3467,12 @@ static keycode_type _get_next_keycode()
return (keyin);
}
-/*
- * Check squares adjacent to player for given feature and return how
- * many there are. If there's only one, return the dx and dy.
- */
+// Check squares adjacent to player for given feature and return how
+// many there are. If there's only one, return the dx and dy.
static int _check_adjacent(dungeon_feature_type feat, int &dx, int &dy)
{
int num = 0;
- int _dx, _dy;
+ int _dx = 0, _dy = 0;
for (int x = -1; x <= 1; x++)
for (int y = -1; y <= 1; y++)
@@ -3494,11 +3492,9 @@ static int _check_adjacent(dungeon_feature_type feat, int &dx, int &dy)
return num;
}
-/*
- Opens doors and handles some aspects of untrapping. If either move_x or
- move_y are non-zero, the pair carries a specific direction for the door
- to be opened (eg if you type ctrl - dir).
- */
+// Opens doors and handles some aspects of untrapping. If either move_x or
+// move_y are non-zero, the pair carries a specific direction for the door
+// to be opened (eg if you type ctrl - dir).
static void _open_door(int move_x, int move_y, bool check_confused)
{
struct dist door_move;
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 64dee389db..17a3c500d8 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -322,10 +322,11 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages)
if (!can_wield(&you.inv[item_slot], true))
return (false);
- // for non-auto_wield cases checked above
+ // For non-auto_wield cases checked above.
if (auto_wield && !check_warning_inscriptions(you.inv[item_slot], OPER_WIELD))
return (false);
+ // Wield the weapon.
if (!safe_to_remove_or_wear(you.inv[item_slot], false))
return (false);
@@ -821,8 +822,12 @@ void wear_armour( int slot ) // slot is for tiles
else if (!armour_prompt("Wear which item?", &armour_wear_2, OPER_WEAR))
return;
- if (safe_to_remove_or_wear( you.inv[armour_wear_2], wearing_slot(slot) ))
+ // Wear the armour.
+ if (safe_to_remove_or_wear( you.inv[armour_wear_2],
+ wearing_slot(armour_wear_2) ))
+ {
do_wear_armour( armour_wear_2, false );
+ }
}
static int armour_equip_delay(const item_def &item)
@@ -2928,6 +2933,7 @@ static bool swap_rings(int ring_slot)
if (!remove_ring(unwanted, false))
return (false);
+ // Put on the new ring.
if (!safe_to_remove_or_wear(you.inv[ring_slot], false))
return (false);
@@ -2983,6 +2989,7 @@ bool puton_item(int item_slot, bool prompt_finger)
return false;
}
+ // Put on the new amulet.
if (!safe_to_remove_or_wear(you.inv[item_slot], false))
return (false);
@@ -2992,6 +2999,7 @@ bool puton_item(int item_slot, bool prompt_finger)
return (true);
}
+ // Put on the amulet.
if (!safe_to_remove_or_wear(you.inv[item_slot], false))
return (false);
@@ -3254,8 +3262,10 @@ bool remove_ring(int slot, bool announce)
if (item_cursed( you.inv[you.equip[hand_used]] ))
{
if (announce)
+ {
mprf("%s is stuck to you!",
you.inv[you.equip[hand_used]].name(DESC_CAP_YOUR).c_str());
+ }
else
mpr("It's stuck to you!");
@@ -3265,6 +3275,7 @@ bool remove_ring(int slot, bool announce)
ring_wear_2 = you.equip[hand_used];
+ // Remove the ring.
if (!safe_to_remove_or_wear(you.inv[ring_wear_2], true))
return (false);
diff --git a/crawl-ref/source/transfor.cc b/crawl-ref/source/transfor.cc
index 6931400de3..d2f7bd9051 100644
--- a/crawl-ref/source/transfor.cc
+++ b/crawl-ref/source/transfor.cc
@@ -27,6 +27,7 @@
#include "misc.h"
#include "output.h"
#include "player.h"
+#include "randart.h"
#include "skills2.h"
#include "stuff.h"
#include "traps.h"
@@ -34,10 +35,67 @@
void drop_everything(void);
void extra_hp(int amount_extra);
+static void _init_equipment_removal(std::set<equipment_type> &rem_stuff,
+ int which_trans)
+{
+ switch (which_trans)
+ {
+ case TRAN_SPIDER:
+ // Spiders CAN wear soft helmets
+ if (you.equip[EQ_HELMET] == -1
+ || !is_hard_helmet(you.inv[you.equip[EQ_HELMET]]))
+ {
+ rem_stuff.erase(EQ_HELMET);
+ }
+ break;
+
+ case TRAN_BAT:
+ // Bats CAN wear soft helmets.
+ if (you.equip[EQ_HELMET] == -1
+ || !is_hard_helmet(you.inv[you.equip[EQ_HELMET]]))
+ {
+ rem_stuff.erase(EQ_HELMET);
+ }
+ break;
+
+ case TRAN_ICE_BEAST:
+ rem_stuff.erase(EQ_CLOAK);
+ // Ice beasts CAN wear soft helmets.
+ if (you.equip[EQ_HELMET] == -1
+ || !is_hard_helmet(you.inv[you.equip[EQ_HELMET]]))
+ {
+ rem_stuff.erase(EQ_HELMET);
+ }
+ break;
+
+ case TRAN_BLADE_HANDS:
+ rem_stuff.erase(EQ_CLOAK);
+ rem_stuff.erase(EQ_HELMET);
+ rem_stuff.erase(EQ_BOOTS);
+ rem_stuff.erase(EQ_BODY_ARMOUR);
+ break;
+
+ case TRAN_STATUE:
+ rem_stuff.erase(EQ_WEAPON); // can still hold a weapon
+ rem_stuff.erase(EQ_CLOAK);
+ rem_stuff.erase(EQ_HELMET);
+ break;
+
+ case TRAN_AIR:
+ // Can't wear anything at all!
+ rem_stuff.insert(EQ_LEFT_RING);
+ rem_stuff.insert(EQ_RIGHT_RING);
+ rem_stuff.insert(EQ_AMULET);
+ break;
+ default:
+ break;
+ }
+}
+
bool remove_equipment(std::set<equipment_type> removed)
{
- if ( removed.find(EQ_WEAPON) != removed.end() &&
- you.equip[EQ_WEAPON] != -1)
+ if (removed.find(EQ_WEAPON) != removed.end()
+ && you.equip[EQ_WEAPON] != -1)
{
unwield_item();
canned_msg(MSG_EMPTY_HANDED);
@@ -45,10 +103,10 @@ bool remove_equipment(std::set<equipment_type> removed)
// Remove items in order (std::set is a sorted container)
std::set<equipment_type>::const_iterator iter;
- for ( iter = removed.begin(); iter != removed.end(); ++iter )
+ for (iter = removed.begin(); iter != removed.end(); ++iter)
{
const equipment_type e = *iter;
- if ( e == EQ_WEAPON || you.equip[e] == -1 )
+ if (e == EQ_WEAPON || you.equip[e] == -1)
continue;
mprf("%s falls away.",
@@ -58,7 +116,7 @@ bool remove_equipment(std::set<equipment_type> removed)
you.equip[e] = -1;
}
- return true;
+ return (true);
} // end remove_equipment()
bool remove_one_equip(equipment_type eq)
@@ -76,7 +134,7 @@ static bool check_for_cursed_equipment(const std::set<equipment_type> &remove)
for (iter = remove.begin(); iter != remove.end(); ++iter )
{
equipment_type e = *iter;
- if ( you.equip[e] == -1 )
+ if (you.equip[e] == -1)
continue;
if (item_cursed( you.inv[ you.equip[e] ] ))
@@ -91,6 +149,78 @@ static bool check_for_cursed_equipment(const std::set<equipment_type> &remove)
return (false);
} // end check_for_cursed_equipment()
+// Count the stat boosts yielded by all items to be removed, and count
+// future losses (caused by the transformation) like a current stat boost,
+// as well. If the sum of all bosts of a stat is equal to or greater than
+// the current stat, give a message and return true.
+bool check_transformation_stat_loss(const std::set<equipment_type> &remove,
+ int str_loss, int dex_loss, int int_loss,
+ bool quiet)
+{
+ // Initialize with additional losses, if any.
+ int prop_str = str_loss;
+ int prop_dex = dex_loss;
+ int prop_int = int_loss;
+
+ // Check over all items to be removed.
+ std::set<equipment_type>::const_iterator iter;
+ for (iter = remove.begin(); iter != remove.end(); ++iter)
+ {
+ equipment_type e = *iter;
+ if (you.equip[e] == -1)
+ continue;
+
+ item_def item = you.inv[you.equip[e]];
+ if (item.base_type == OBJ_JEWELLERY)
+ {
+ if (!item_ident( item, ISFLAG_KNOW_PLUSES ))
+ continue;
+
+ switch (item.sub_type)
+ {
+ case RING_STRENGTH:
+ if (item.plus != 0)
+ prop_str += item.plus;
+ break;
+ case RING_DEXTERITY:
+ if (item.plus != 0)
+ prop_dex += item.plus;
+ break;
+ case RING_INTELLIGENCE:
+ if (item.plus != 0)
+ prop_int += item.plus;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (is_random_artefact( item ))
+ {
+ prop_str += randart_known_wpn_property(item, RAP_STRENGTH);
+ prop_int += randart_known_wpn_property(item, RAP_INTELLIGENCE);
+ prop_dex += randart_known_wpn_property(item, RAP_DEXTERITY);
+ }
+
+ // Since there might be multiple items whose effects cancel each other
+ // out while worn, if at any point in the order of checking this list
+ // (which is the same order as when removing items) one of your stats
+ // would reach 0, return true.
+ if (prop_str >= you.strength || prop_int >= you.intel
+ || prop_dex >= you.dex)
+ {
+ if (!quiet)
+ {
+ mpr("This transformation would result in fatal stat loss!",
+ MSGCH_WARN);
+ }
+ return (true);
+ }
+ }
+
+ return (false);
+}
+
// FIXME: Switch to 4.1 transforms handling.
size_type transform_size(int psize)
{
@@ -163,10 +293,10 @@ bool transform(int pow, transformation_type which_trans)
return (false);
}
- //jmf: silently discard this enchantment
+ //jmf: Silently discard this enchantment
you.duration[DUR_STONESKIN] = 0;
- // We drop everything except jewellery by default
+ // We drop everything except jewellery by default.
equipment_type default_rem[] = {
EQ_WEAPON, EQ_CLOAK, EQ_HELMET, EQ_GLOVES, EQ_BOOTS,
EQ_SHIELD, EQ_BODY_ARMOUR
@@ -174,6 +304,7 @@ bool transform(int pow, transformation_type which_trans)
std::set<equipment_type> rem_stuff(default_rem,
default_rem + ARRAYSZ(default_rem));
+ _init_equipment_removal(rem_stuff, which_trans);
you.redraw_evasion = true;
you.redraw_armour_class = true;
@@ -183,13 +314,6 @@ bool transform(int pow, transformation_type which_trans)
switch (which_trans)
{
case TRAN_SPIDER: // also AC +3, ev +3, fast_run
- // spiders CAN wear soft helmets
- if ( you.equip[EQ_HELMET] == -1
- || !is_hard_helmet(you.inv[you.equip[EQ_HELMET]]))
- {
- rem_stuff.erase(EQ_HELMET);
- }
-
if (check_for_cursed_equipment( rem_stuff ))
return (false);
@@ -210,16 +334,11 @@ bool transform(int pow, transformation_type which_trans)
return (true);
case TRAN_BAT:
- // bats CAN wear soft helmets
- if ( you.equip[EQ_HELMET] == -1
- || !is_hard_helmet(you.inv[you.equip[EQ_HELMET]]))
- {
- rem_stuff.erase(EQ_HELMET);
- }
+ if (check_for_cursed_equipment(rem_stuff))
+ return (false);
- // high ev, low ac, high speed
- if (check_for_cursed_equipment( rem_stuff ))
- return false;
+ if (check_transformation_stat_loss(rem_stuff, 5)) // Str loss = 5
+ return (false);
mprf("You turn into a %sbat.",
you.species == SP_VAMPIRE ? "vampire " : "");
@@ -232,6 +351,7 @@ bool transform(int pow, transformation_type which_trans)
if (you.duration[DUR_TRANSFORMATION] > 100)
you.duration[DUR_TRANSFORMATION] = 100;
+ // high ev, low ac, high speed
modify_stat( STAT_DEXTERITY, 5, true,
"gaining the bat transformation");
modify_stat( STAT_STRENGTH, -5, true,
@@ -242,16 +362,8 @@ bool transform(int pow, transformation_type which_trans)
return (true);
case TRAN_ICE_BEAST: // also AC +3, cold +3, fire -1, pois +1
- rem_stuff.erase(EQ_CLOAK);
- // ice beasts CAN wear soft helmets
- if ( you.equip[EQ_HELMET] == -1
- || !is_hard_helmet(you.inv[you.equip[EQ_HELMET]]))
- {
- rem_stuff.erase(EQ_HELMET);
- }
-
if (check_for_cursed_equipment( rem_stuff ))
- return false;
+ return (false);
mpr( "You turn into a creature of crystalline ice." );
@@ -292,12 +404,11 @@ bool transform(int pow, transformation_type which_trans)
return (true);
case TRAN_STATUE: // also AC +20, ev -5, elec +1, pois +1, neg +1, slow
- rem_stuff.erase(EQ_WEAPON); // can still hold a weapon
- rem_stuff.erase(EQ_CLOAK);
- rem_stuff.erase(EQ_HELMET);
-
if (check_for_cursed_equipment( rem_stuff ))
- return false;
+ return (false);
+
+ if (check_transformation_stat_loss(rem_stuff, 0, 2)) // Dex loss = 2
+ return (false);
if (you.species == SP_GNOME && coinflip())
mpr( "Look, a garden gnome. How cute!" );
@@ -306,7 +417,7 @@ bool transform(int pow, transformation_type which_trans)
else
mpr( "You turn into a living statue of rough stone." );
- // too stiff to make use of shields, gloves, or armour -- bwr
+ // Too stiff to make use of shields, gloves, or armour -- bwr
remove_equipment( rem_stuff );
you.attribute[ATTR_TRANSFORMATION] = TRAN_STATUE;
@@ -333,8 +444,10 @@ bool transform(int pow, transformation_type which_trans)
return false;
if (you.species == SP_MERFOLK && player_is_swimming())
+ {
mpr("You fly out of the water as you turn into "
"a fearsome dragon!");
+ }
else
mpr("You turn into a fearsome dragon!");
@@ -364,6 +477,8 @@ bool transform(int pow, transformation_type which_trans)
return (true);
case TRAN_LICH:
+ // Don't need to remove anything.
+
// also AC +3, cold +1, neg +3, pois +1, is_undead, res magic +50,
// spec_death +1, and drain attack (if empty-handed)
if (you.duration[DUR_DEATHS_DOOR])
@@ -403,10 +518,6 @@ bool transform(int pow, transformation_type which_trans)
return (true);
case TRAN_AIR:
- rem_stuff.insert(EQ_LEFT_RING);
- rem_stuff.insert(EQ_RIGHT_RING);
- rem_stuff.insert(EQ_AMULET);
-
if (check_for_cursed_equipment( rem_stuff ))
return false;
diff --git a/crawl-ref/source/transfor.h b/crawl-ref/source/transfor.h
index 12a1edc83a..933f512cb8 100644
--- a/crawl-ref/source/transfor.h
+++ b/crawl-ref/source/transfor.h
@@ -48,6 +48,9 @@ void untransform(void);
* called from: item_use
* *********************************************************************** */
bool can_equip(equipment_type use_which, bool ignore_temporary);
+bool check_transformation_stat_loss(const std::set<equipment_type> &remove,
+ int str_loss = 0, int dex_loss = 0,
+ int int_loss = 0, bool quiet = false);
size_type transform_size(int psize = PSIZE_BODY);
// last updated 12may2000 {dlb}