summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/transform.cc
diff options
context:
space:
mode:
authorAdam Borowski <kilobyte@angband.pl>2010-01-07 21:35:39 +0100
committerAdam Borowski <kilobyte@angband.pl>2010-01-07 21:35:39 +0100
commitf536c278597be3ae8664785ba1997085f177e89b (patch)
tree3c3baf1e2b0d2c046e07ae4cb179dcb60c5806e6 /crawl-ref/source/transform.cc
parent81ae2f6d115443d5e75ae4a4c4bb9e3629e93fe7 (diff)
downloadcrawl-ref-f536c278597be3ae8664785ba1997085f177e89b.tar.gz
crawl-ref-f536c278597be3ae8664785ba1997085f177e89b.zip
Rename transfor.{cc,h} to transform.{cc,h}.
Diffstat (limited to 'crawl-ref/source/transform.cc')
-rw-r--r--crawl-ref/source/transform.cc1045
1 files changed, 1045 insertions, 0 deletions
diff --git a/crawl-ref/source/transform.cc b/crawl-ref/source/transform.cc
new file mode 100644
index 0000000000..ad8e8545ca
--- /dev/null
+++ b/crawl-ref/source/transform.cc
@@ -0,0 +1,1045 @@
+/*
+ * File: transform.cc
+ * Summary: Misc function related to player transformations.
+ * Written by: Linley Henzell
+ */
+
+#include "AppHdr.h"
+
+#include "transform.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "externs.h"
+
+#include "artefact.h"
+#include "delay.h"
+#include "invent.h"
+#include "it_use2.h"
+#include "item_use.h"
+#include "itemprop.h"
+#include "items.h"
+#include "output.h"
+#include "player.h"
+#include "random.h"
+#include "skills2.h"
+#include "state.h"
+#include "stuff.h"
+#include "traps.h"
+#include "xom.h"
+
+static void _extra_hp(int amount_extra);
+
+bool transformation_can_wield(transformation_type trans)
+{
+ return (trans == TRAN_NONE
+ || trans == TRAN_STATUE
+ || trans == TRAN_LICH);
+}
+
+bool transform_allows_wearing_item(const item_def& item)
+{
+ return (
+ transform_allows_wearing_item(item,
+ static_cast<transformation_type>(
+ you.attribute[ATTR_TRANSFORMATION])));
+}
+
+bool transform_allows_wearing_item(const item_def& item,
+ transformation_type transform)
+{
+ bool rc = true;
+
+ if (item.base_type == OBJ_JEWELLERY)
+ {
+ // Everything but bats can wear all jewellery; bats and pigs can
+ // only wear amulets.
+ if ((transform == TRAN_BAT || transform == TRAN_PIG)
+ && !jewellery_is_amulet(item))
+ {
+ rc = false;
+ }
+ }
+ else
+ {
+ // It's not jewellery, and it's worn, so it must be armour.
+ const equipment_type eqslot = get_armour_slot(item);
+ const bool is_soft_helmet = is_helmet(item) && !is_hard_helmet(item);
+
+ switch (transform)
+ {
+ // Some forms can wear everything.
+ case TRAN_NONE:
+ case TRAN_LICH:
+ rc = true;
+ break;
+
+ // Some can't wear anything.
+ case TRAN_DRAGON:
+ case TRAN_BAT:
+ case TRAN_PIG:
+ rc = false;
+ break;
+
+ // And some need more complicated logic.
+ case TRAN_SPIDER:
+ rc = is_soft_helmet;
+ break;
+
+ case TRAN_BLADE_HANDS:
+ rc = (eqslot != EQ_SHIELD && eqslot != EQ_GLOVES);
+ break;
+
+ case TRAN_STATUE:
+ rc = (eqslot == EQ_CLOAK || eqslot == EQ_HELMET);
+ break;
+
+ case TRAN_ICE_BEAST:
+ rc = (eqslot == EQ_CLOAK || is_soft_helmet);
+ break;
+
+ default: // Bug-catcher.
+ mprf(MSGCH_ERROR, "Unknown transformation type %d in "
+ "transform_allows_wearing_item",
+ you.attribute[ATTR_TRANSFORMATION]);
+ break;
+ }
+ }
+
+ return (rc);
+}
+
+static std::set<equipment_type>
+_init_equipment_removal(transformation_type trans)
+{
+ std::set<equipment_type> result;
+ if (!transformation_can_wield(trans) && you.weapon())
+ result.insert(EQ_WEAPON);
+
+ // Liches can't wield holy weapons.
+ if (trans == TRAN_LICH
+ && you.weapon()
+ && get_weapon_brand(*you.weapon()) == SPWPN_HOLY_WRATH)
+ {
+ result.insert(EQ_WEAPON);
+ }
+
+ for (int i = EQ_WEAPON + 1; i < NUM_EQUIP; ++i)
+ {
+ const equipment_type eq = static_cast<equipment_type>(i);
+ const item_def *pitem = you.slot_item(eq);
+ if (pitem && !transform_allows_wearing_item(*pitem, trans))
+ result.insert(eq);
+ }
+ return (result);
+}
+
+static void _unwear_equipment_slot(equipment_type eqslot)
+{
+ const int slot = you.equip[eqslot];
+ item_def *item = you.slot_item(eqslot);
+ if (item == NULL)
+ return;
+
+ if (eqslot == EQ_WEAPON)
+ {
+ unwield_item(!you.berserk());
+ canned_msg(MSG_EMPTY_HANDED);
+ you.attribute[ATTR_WEAPON_SWAP_INTERRUPTED] = slot + 1;
+ }
+ else if (item->base_type == OBJ_JEWELLERY)
+ jewellery_remove_effects(*item, false);
+ else
+ unwear_armour(slot);
+}
+
+static void _remove_equipment(const std::set<equipment_type>& removed,
+ bool meld = true, bool mutation = false)
+{
+ // Meld items into you in (reverse) order. (std::set is a sorted container)
+ std::set<equipment_type>::const_iterator iter;
+ for (iter = removed.begin(); iter != removed.end(); ++iter)
+ {
+ const equipment_type e = *iter;
+ item_def *equip = you.slot_item(e);
+ if (equip == NULL)
+ continue;
+
+ bool unequip = (e == EQ_WEAPON || !meld);
+
+ mprf("%s %s%s %s", equip->name(DESC_CAP_YOUR).c_str(),
+ unequip ? "fall" : "meld",
+ equip->quantity > 1 ? "" : "s",
+ unequip ? "away!" : "into your body.");
+
+ _unwear_equipment_slot(e);
+
+ if (unequip)
+ {
+ you.equip[e] = -1;
+
+ if (mutation)
+ {
+ // A mutation made us not only lose an equipment slot
+ // but actually removed a worn item: Funny!
+ xom_is_stimulated(is_artefact(*equip) ? 255 : 128);
+ }
+ }
+ }
+}
+
+// FIXME: merge this with you_can_wear(), can_wear_armour(), etc.
+bool _mutations_prevent_wearing(const item_def& item)
+{
+ const equipment_type eqslot = get_armour_slot(item);
+
+ if (is_hard_helmet(item)
+ && (player_mutation_level(MUT_HORNS)
+ || player_mutation_level(MUT_BEAK)))
+ {
+ return (true);
+ }
+
+ // Barding is excepted here.
+ if (item.sub_type == ARM_BOOTS
+ && (player_mutation_level(MUT_HOOVES)
+ || player_mutation_level(MUT_TALONS)))
+ {
+ return (true);
+ }
+
+ if (eqslot == EQ_GLOVES && player_mutation_level(MUT_CLAWS) >= 3)
+ return (true);
+
+ return (false);
+}
+
+static void _rewear_equipment_slot(equipment_type e)
+{
+ if (e == EQ_WEAPON) // shouldn't happen
+ return;
+
+ if (you.equip[e] == -1)
+ return;
+
+ item_def& item = you.inv[you.equip[e]];
+
+ if (item.base_type == OBJ_JEWELLERY)
+ jewellery_wear_effects(item);
+ else
+ {
+ // In case the player was mutated during the transformation,
+ // check whether the equipment is still wearable.
+ bool force_remove = _mutations_prevent_wearing(item);
+
+ // If you switched weapons during the transformation, make
+ // sure you can still wear your shield.
+ // (This is only possible with Statue Form.)
+ if (e == EQ_SHIELD && you.weapon()
+ && is_shield_incompatible(*you.weapon(), &item))
+ {
+ force_remove = true;
+ }
+
+ if (force_remove)
+ {
+ mprf("%s is pushed off your body!",
+ item.name(DESC_CAP_YOUR).c_str());
+ you.equip[e] = -1;
+ }
+ else
+ armour_wear_effects(you.equip[e]);
+ }
+}
+
+static void _unmeld_equipment(const std::set<equipment_type>& melded)
+{
+ // Unmeld items in order.
+ std::set<equipment_type>::const_iterator iter;
+ for (iter = melded.begin(); iter != melded.end(); ++iter)
+ {
+ const equipment_type e = *iter;
+ if (e == EQ_WEAPON || you.equip[e] == -1)
+ continue;
+
+ _rewear_equipment_slot(e);
+ }
+}
+
+void unmeld_one_equip(equipment_type eq)
+{
+ std::set<equipment_type> e;
+ e.insert(eq);
+ _unmeld_equipment(e);
+}
+
+void remove_one_equip(equipment_type eq, bool meld, bool mutation)
+{
+ std::set<equipment_type> r;
+ r.insert(eq);
+ _remove_equipment(r, meld, mutation);
+}
+
+static bool _tran_may_meld_cursed(int transformation)
+{
+ switch (transformation)
+ {
+ case TRAN_BAT:
+ // Vampires of sufficient level may transform into bats even
+ // with cursed gear.
+ if (you.species == SP_VAMPIRE && you.experience_level >= 10)
+ return (true);
+ // intentional fall-through
+ case TRAN_SPIDER:
+ return (false);
+ default:
+ return (true);
+ }
+}
+
+// Returns true if any piece of equipment that has to be removed is cursed.
+// Useful for keeping low level transformations from being too useful.
+static bool _check_for_cursed_equipment(const std::set<equipment_type> &remove,
+ const int trans, bool quiet = false)
+{
+ std::set<equipment_type>::const_iterator iter;
+ for (iter = remove.begin(); iter != remove.end(); ++iter)
+ {
+ const equipment_type e = *iter;
+ if (you.equip[e] == -1)
+ continue;
+
+ const item_def& item = you.inv[ you.equip[e] ];
+ if (item.cursed())
+ {
+ if (e != EQ_WEAPON && _tran_may_meld_cursed(trans))
+ continue;
+
+ // Wielding a cursed non-weapon/non-staff won't hinder
+ // transformations.
+ if (e == EQ_WEAPON && item.base_type != OBJ_WEAPONS
+ && item.base_type != OBJ_STAVES)
+ {
+ continue;
+ }
+
+ if (!quiet)
+ {
+ mpr( "Your cursed equipment won't allow you to complete the "
+ "transformation." );
+ }
+
+ return (true);
+ }
+ }
+ return (false);
+}
+
+// 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 boosts 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,
+ bool quiet, int str_loss, int dex_loss,
+ int int_loss)
+{
+ // Initialise with additional losses, if any.
+ int prop_str = str_loss;
+ int prop_dex = dex_loss;
+ int prop_int = int_loss;
+
+ // Might is very much temporary and might run out at any point during
+ // your transformation, possibly resulting in stat loss caused by a
+ // combination of an unequipping (and/or stat lowering) transformation
+ // and Might running out at an inopportune moment.
+ if (you.duration[DUR_MIGHT])
+ prop_str += 5;
+ if (you.duration[DUR_BRILLIANCE])
+ prop_int += 5;
+ if (you.duration[DUR_AGILITY])
+ prop_dex += 5;
+
+ 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);
+ }
+
+ // Check over all items to be removed or melded.
+ 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;
+
+ const item_def& item = you.inv[you.equip[e]];
+
+ // There are no stat-boosting non-weapons/non-staves.
+ if (e == EQ_WEAPON
+ && item.base_type != OBJ_WEAPONS && item.base_type != OBJ_STAVES)
+ {
+ continue;
+ }
+
+ // Currently, the only non-artefacts which have stat-changing
+ // effects are rings.
+ if (item.base_type == OBJ_JEWELLERY)
+ {
+ if (!item_ident(item, ISFLAG_KNOW_PLUSES))
+ continue;
+
+ switch (item.sub_type)
+ {
+ case RING_STRENGTH: prop_str += item.plus; break;
+ case RING_DEXTERITY: prop_dex += item.plus; break;
+ case RING_INTELLIGENCE: prop_int += item.plus; break;
+ default: break;
+ }
+ }
+ else if (item.base_type == OBJ_ARMOUR)
+ {
+ switch (get_armour_ego_type( item ))
+ {
+ case SPARM_STRENGTH: prop_str += 3; break;
+ case SPARM_DEXTERITY: prop_dex += 3; break;
+ case SPARM_INTELLIGENCE: prop_int += 3; break;
+ default: break;
+ }
+ }
+
+ if (is_artefact(item))
+ {
+ prop_str += artefact_known_wpn_property(item, ARTP_STRENGTH);
+ prop_int += artefact_known_wpn_property(item, ARTP_INTELLIGENCE);
+ prop_dex += artefact_known_wpn_property(item, ARTP_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);
+}
+
+// Returns true if the player got prompted by an inscription warning and
+// chose to opt out.
+bool _check_transformation_inscription_warning(
+ const std::set<equipment_type> &remove)
+{
+ // Check over all items to be removed or melded.
+ 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;
+
+ const item_def& item = you.inv[you.equip[e]];
+
+ operation_types op = OPER_WEAR;
+ if (e == EQ_WEAPON)
+ op = OPER_WIELD;
+ else if (item.base_type == OBJ_JEWELLERY)
+ op = OPER_PUTON;
+
+ if (!check_old_item_warning(item, op))
+ return (true);
+ }
+
+ return (false);
+}
+
+// FIXME: Switch to 4.1 transforms handling.
+size_type transform_size(int psize)
+{
+ return you.transform_size(psize);
+}
+
+size_type player::transform_size(int psize) const
+{
+ const int transform = attribute[ATTR_TRANSFORMATION];
+ switch (transform)
+ {
+ case TRAN_SPIDER:
+ case TRAN_BAT:
+ return SIZE_TINY;
+ case TRAN_PIG:
+ return SIZE_SMALL;
+ case TRAN_ICE_BEAST:
+ return SIZE_LARGE;
+ case TRAN_DRAGON:
+ return SIZE_HUGE;
+ default:
+ return SIZE_CHARACTER;
+ }
+}
+
+void transformation_expiration_warning()
+{
+ if (you.duration[DUR_TRANSFORMATION]
+ <= get_expiration_threshold(DUR_TRANSFORMATION))
+ {
+ mpr("You have a feeling this form won't last long.");
+ }
+}
+
+static bool _abort_or_fizzle(bool just_check)
+{
+ if (!just_check && you.turn_is_over)
+ {
+ canned_msg(MSG_SPELL_FIZZLES);
+ return (true); // pay the necessary costs
+ }
+ return (false); // SPRET_ABORT
+}
+
+// Transforms you into the specified form. If force is true, checks for
+// inscription warnings are skipped, and the transformation fails silently
+// (if it fails). If just_check is true the transformation doesn't actually
+// happen, but the method returns whether it would be successful.
+bool transform(int pow, transformation_type which_trans, bool force,
+ bool just_check)
+{
+ if (!force && crawl_state.is_god_acting())
+ force = true;
+
+ if (!force && you.transform_uncancellable)
+ {
+ // Jiyva's wrath-induced transformation is blocking the attempt.
+ // May need to be updated if transform_uncancellable is used for
+ // other uses.
+ return (false);
+ }
+
+ if (you.species == SP_MERFOLK && you.swimming()
+ && which_trans != TRAN_DRAGON && which_trans != TRAN_BAT)
+ {
+ // This might be overkill, but it's okay because obviously
+ // whatever magical ability that lets them walk on land is
+ // removed when they're in water (in this case, their natural
+ // form is completely over-riding any other... goes well with
+ // the forced transform when entering water)... but merfolk can
+ // transform into flying forms.
+ if (!force)
+ mpr("You cannot transform out of your normal form while in water.");
+ return (false);
+ }
+
+ // This must occur before the untransform() and the is_undead check.
+ if (you.attribute[ATTR_TRANSFORMATION]
+ == static_cast<unsigned>(which_trans))
+ {
+ if (you.duration[DUR_TRANSFORMATION] < 100 * BASELINE_DELAY)
+ {
+ if (just_check)
+ return (true);
+
+ if (which_trans==TRAN_PIG)
+ mpr("You feel you'll be a pig longer.");
+ else
+ mpr("You extend your transformation's duration.");
+ you.increase_duration(DUR_TRANSFORMATION, random2(pow), 100);
+
+ return (true);
+ }
+ else
+ {
+ if (!force && which_trans!=TRAN_PIG)
+ mpr("You cannot extend your transformation any further!");
+ return (false);
+ }
+ }
+
+ // The actual transformation may still fail later (e.g. due to cursed
+ // equipment). Ideally, untransforming should cost a turn but nothing
+ // else (as does the "End Transformation" ability). As it is, you
+ // pay with mana and hunger if you already untransformed.
+ if (!just_check && you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE)
+ {
+ bool skip_wielding = false;
+ switch (which_trans)
+ {
+ case TRAN_STATUE:
+ case TRAN_LICH:
+ break;
+ default:
+ skip_wielding = true;
+ break;
+ }
+ // Skip wielding weapon if it gets unwielded again right away.
+ untransform(skip_wielding);
+ }
+
+ // Catch some conditions which prevent transformation.
+ if (you.is_undead
+ && (you.species != SP_VAMPIRE
+ || which_trans != TRAN_BAT && you.hunger_state <= HS_SATIATED))
+ {
+ if (!force)
+ mpr("Your unliving flesh cannot be transformed in this way.");
+ return (_abort_or_fizzle(just_check));
+ }
+
+ if (which_trans == TRAN_LICH && you.duration[DUR_DEATHS_DOOR])
+ {
+ if (!force)
+ {
+ mpr("The transformation conflicts with an enchantment "
+ "already in effect.");
+ }
+ return (_abort_or_fizzle(just_check));
+ }
+
+ std::set<equipment_type> rem_stuff = _init_equipment_removal(which_trans);
+
+ if (which_trans != TRAN_PIG
+ && _check_for_cursed_equipment(rem_stuff, which_trans, force))
+ {
+ return (_abort_or_fizzle(just_check));
+ }
+
+ int str = 0, dex = 0, symbol = '@', colour = LIGHTGREY, xhp = 0, dur = 0;
+ const char* tran_name = "buggy";
+ const char* msg = "You transform into something buggy!";
+ switch (which_trans)
+ {
+ case TRAN_SPIDER:
+ tran_name = "spider";
+ dex = 5;
+ symbol = 's';
+ colour = BROWN;
+ dur = std::min(10 + random2(pow) + random2(pow), 60);
+ msg = "You turn into a venomous arachnid creature.";
+ break;
+
+ case TRAN_BLADE_HANDS:
+ tran_name = "Blade Hands";
+ dur = std::min(10 + random2(pow), 100);
+ msg = "Your hands turn into razor-sharp scythe blades.";
+ break;
+
+ case TRAN_STATUE:
+ tran_name = "statue";
+ str = 2;
+ dex = -2;
+ xhp = 15;
+ symbol = '8';
+ colour = LIGHTGREY;
+ dur = std::min(20 + random2(pow) + random2(pow), 100);
+ if (player_genus(GENPC_DWARVEN) && one_chance_in(10))
+ msg = "You inwardly fear your resemblance to a lawn ornament.";
+ else
+ msg = "You turn into a living statue of rough stone.";
+ break;
+
+ case TRAN_ICE_BEAST:
+ tran_name = "ice beast";
+ xhp = 12;
+ symbol = 'I';
+ colour = WHITE;
+ dur = std::min(30 + random2(pow) + random2(pow), 100);
+ msg = "You turn into a creature of crystalline ice.";
+ break;
+
+ case TRAN_DRAGON:
+ tran_name = "dragon";
+ str = 10;
+ xhp = 16;
+ symbol = 'D';
+ colour = GREEN;
+ dur = std::min(20 + random2(pow) + random2(pow), 100);
+ if (you.species == SP_MERFOLK && you.swimming())
+ {
+ msg = "You fly out of the water as you turn into "
+ "a fearsome dragon!";
+ }
+ else
+ msg = "You turn into a fearsome dragon!";
+ break;
+
+ case TRAN_LICH:
+ tran_name = "lich";
+ str = 3;
+ symbol = 'L';
+ colour = LIGHTGREY;
+ dur = std::min(20 + random2(pow) + random2(pow), 100);
+ msg = "Your body is suffused with negative energy!";
+ break;
+
+ case TRAN_BAT:
+ tran_name = "bat";
+ str = -5;
+ dex = 5;
+ symbol = 'b';
+ colour = (you.species == SP_VAMPIRE ? DARKGREY : LIGHTGREY);
+ dur = std::min(20 + random2(pow) + random2(pow), 100);
+ if (you.species == SP_VAMPIRE)
+ msg = "You turn into a vampire bat.";
+ else
+ msg = "You turn into a bat.";
+ break;
+
+ case TRAN_PIG:
+ tran_name = "pig";
+ symbol = 'h';
+ colour = RED;
+ dur = pow;
+ msg = "You have been turned into a pig!";
+ you.transform_uncancellable = true;
+ break;
+
+ case TRAN_NONE:
+ case NUM_TRANSFORMATIONS:
+ break;
+ }
+
+ if (check_transformation_stat_loss(rem_stuff, force || which_trans == TRAN_PIG,
+ std::max(-str, 0), std::max(-dex,0)))
+ { // would have died to stat loss
+ if (which_trans == TRAN_PIG)
+ { // no easy way around this!
+ mpr("A dreadful feeling locks you in place!");
+ if (you.duration[DUR_PARALYSIS]<10 * BASELINE_DELAY)
+ you.duration[DUR_PARALYSIS]=10 * BASELINE_DELAY;
+ }
+ return (_abort_or_fizzle(just_check));
+ }
+
+ // If we're just pretending return now.
+ if (just_check)
+ return (true);
+
+ if (!force && _check_transformation_inscription_warning(rem_stuff))
+ return (_abort_or_fizzle(just_check));
+
+ // All checks done, transformation will take place now.
+ you.redraw_evasion = true;
+ you.redraw_armour_class = true;
+ you.wield_change = true;
+
+ // Most transformations conflict with stone skin.
+ if (which_trans != TRAN_NONE
+ && which_trans != TRAN_BLADE_HANDS
+ && which_trans != TRAN_STATUE)
+ {
+ you.duration[DUR_STONESKIN] = 0;
+ }
+
+ // Give the transformation message.
+ mpr(msg);
+
+ _remove_equipment(rem_stuff);
+
+ // Update your status.
+ you.attribute[ATTR_TRANSFORMATION] = which_trans;
+ you.set_duration(DUR_TRANSFORMATION, dur);
+ you.symbol = symbol;
+ you.colour = colour;
+
+ burden_change();
+
+ if (str)
+ {
+ modify_stat(STAT_STRENGTH, str, true,
+ make_stringf("gaining the %s transformation",
+ tran_name).c_str());
+ }
+
+ if (dex)
+ {
+ modify_stat(STAT_DEXTERITY, dex, true,
+ make_stringf("gaining the %s transformation",
+ tran_name).c_str());
+ }
+
+ if (xhp)
+ _extra_hp(xhp);
+
+ // Extra effects
+ switch (which_trans)
+ {
+ case TRAN_STATUE:
+ if (you.duration[DUR_STONEMAIL] || you.duration[DUR_STONESKIN])
+ mpr("Your new body merges with your stone armour.");
+ break;
+
+ case TRAN_ICE_BEAST:
+ if (you.duration[DUR_ICY_ARMOUR])
+ mpr("Your new body merges with your icy armour.");
+ break;
+
+ case TRAN_DRAGON:
+ if (you.attribute[ATTR_HELD])
+ {
+ mpr("The net rips apart!");
+ you.attribute[ATTR_HELD] = 0;
+ int net = get_trapping_net(you.pos());
+ if (net != NON_ITEM)
+ destroy_item(net);
+ }
+ break;
+
+ case TRAN_LICH:
+ // undead cannot regenerate -- bwr
+ if (you.duration[DUR_REGENERATION])
+ {
+ mpr("You stop regenerating.", MSGCH_DURATION);
+ you.duration[DUR_REGENERATION] = 0;
+ }
+
+ // silently removed since undead automatically resist poison -- bwr
+ you.duration[DUR_RESIST_POISON] = 0;
+
+ you.is_undead = US_UNDEAD;
+ you.hunger_state = HS_SATIATED; // no hunger effects while transformed
+ set_redraw_status(REDRAW_HUNGER);
+ break;
+
+ default:
+ break;
+ }
+
+ // This only has an effect if the transformation happens passively,
+ // for example if Xom decides to transform you while you're busy
+ // running around or butchering corpses.
+ stop_delay();
+
+ if (you.species != SP_VAMPIRE || which_trans != TRAN_BAT)
+ transformation_expiration_warning();
+
+ return (true);
+}
+
+bool transform_can_butcher_barehanded(transformation_type tt)
+{
+ return (tt == TRAN_BLADE_HANDS || tt == TRAN_DRAGON || tt == TRAN_ICE_BEAST);
+}
+
+void untransform(bool skip_wielding)
+{
+ const flight_type old_flight = you.flight_mode();
+
+ you.redraw_evasion = true;
+ you.redraw_armour_class = true;
+ you.wield_change = true;
+
+ you.symbol = '@';
+ you.colour = LIGHTGREY;
+
+ // Must be unset first or else infinite loops might result. -- bwr
+ const transformation_type old_form =
+ static_cast<transformation_type>(you.attribute[ ATTR_TRANSFORMATION ]);
+
+ // We may have to unmeld a couple of equipment types.
+ std::set<equipment_type> melded = _init_equipment_removal(old_form);
+
+ you.attribute[ATTR_TRANSFORMATION] = TRAN_NONE;
+ you.duration[DUR_TRANSFORMATION] = 0;
+
+ burden_change();
+
+ int hp_downscale = 10;
+
+ switch (old_form)
+ {
+ case TRAN_SPIDER:
+ mpr("Your transformation has ended.", MSGCH_DURATION);
+ modify_stat( STAT_DEXTERITY, -5, true,
+ "losing the spider transformation" );
+ break;
+
+ case TRAN_BAT:
+ mpr("Your transformation has ended.", MSGCH_DURATION);
+ modify_stat( STAT_DEXTERITY, -5, true,
+ "losing the bat transformation" );
+ modify_stat( STAT_STRENGTH, 5, true,
+ "losing the bat transformation" );
+ break;
+
+ case TRAN_BLADE_HANDS:
+ mpr( "Your hands revert to their normal proportions.", MSGCH_DURATION );
+ you.wield_change = true;
+ break;
+
+ case TRAN_STATUE:
+ mpr( "You revert to your normal fleshy form.", MSGCH_DURATION );
+ modify_stat( STAT_DEXTERITY, 2, true,
+ "losing the statue transformation" );
+ modify_stat( STAT_STRENGTH, -2, true,
+ "losing the statue transformation");
+
+ // Note: if the core goes down, the combined effect soon disappears,
+ // but the reverse isn't true. -- bwr
+ if (you.duration[DUR_STONEMAIL])
+ you.duration[DUR_STONEMAIL] = 1;
+
+ if (you.duration[DUR_STONESKIN])
+ you.duration[DUR_STONESKIN] = 1;
+
+ hp_downscale = 15;
+ break;
+
+ case TRAN_ICE_BEAST:
+ mpr( "You warm up again.", MSGCH_DURATION );
+
+ // Note: if the core goes down, the combined effect soon disappears,
+ // but the reverse isn't true. -- bwr
+ if (you.duration[DUR_ICY_ARMOUR])
+ you.duration[DUR_ICY_ARMOUR] = 1;
+
+ hp_downscale = 12;
+ break;
+
+ case TRAN_DRAGON:
+ mpr( "Your transformation has ended.", MSGCH_DURATION );
+ modify_stat(STAT_STRENGTH, -10, true,
+ "losing the dragon transformation" );
+ hp_downscale = 16;
+ break;
+
+ case TRAN_LICH:
+ mpr( "You feel yourself come back to life.", MSGCH_DURATION );
+ modify_stat(STAT_STRENGTH, -3, true,
+ "losing the lich transformation" );
+ you.is_undead = US_ALIVE;
+ break;
+
+ case TRAN_PIG:
+ mpr( "Your transformation has ended.", MSGCH_DURATION );
+ break;
+
+ default:
+ break;
+ }
+
+ _unmeld_equipment(melded);
+
+ // Re-check terrain now that be may no longer be flying.
+ if (old_flight && you.flight_mode() == FL_NONE)
+ move_player_to_grid(you.pos(), false, true, true);
+
+ if (transform_can_butcher_barehanded(old_form))
+ stop_butcher_delay();
+
+ // If nagas wear boots while transformed, they fall off again afterwards:
+ // I don't believe this is currently possible, and if it is we
+ // probably need something better to cover all possibilities. -bwr
+
+ // Removed barding check, no transformed creatures can wear barding
+ // anyway.
+ // *coughs* Ahem, blade hands... -- jpeg
+ if (you.species == SP_NAGA || you.species == SP_CENTAUR)
+ {
+ const int arm = you.equip[EQ_BOOTS];
+
+ if (arm != -1 && you.inv[arm].sub_type == ARM_BOOTS)
+ remove_one_equip(EQ_BOOTS);
+ }
+
+ if (hp_downscale != 10 && you.hp != you.hp_max)
+ {
+ you.hp = you.hp * 10 / hp_downscale;
+ if (you.hp < 1)
+ you.hp = 1;
+ else if (you.hp > you.hp_max)
+ you.hp = you.hp_max;
+ }
+ calc_hp();
+
+ if (!skip_wielding)
+ handle_interrupted_swap(true, false, true);
+
+ you.turn_is_over = true;
+ if (you.transform_uncancellable)
+ you.transform_uncancellable = false;
+}
+
+// XXX: This whole system is a mess as it still relies on special
+// cases to handle a large number of things (see wear_armour()) -- bwr
+bool can_equip( equipment_type use_which, bool ignore_temporary )
+{
+ if (use_which == EQ_HELMET
+ && (player_mutation_level(MUT_HORNS)
+ || player_mutation_level(MUT_BEAK)))
+ {
+ return (false);
+ }
+
+ if (use_which == EQ_BOOTS && !player_has_feet())
+ return (false);
+
+ if (use_which == EQ_GLOVES && you.has_claws(false) >= 3)
+ return (false);
+
+ if (!ignore_temporary)
+ {
+ switch (you.attribute[ATTR_TRANSFORMATION])
+ {
+ case TRAN_NONE:
+ case TRAN_LICH:
+ return (true);
+
+ case TRAN_BLADE_HANDS:
+ return (use_which != EQ_WEAPON
+ && use_which != EQ_GLOVES
+ && use_which != EQ_SHIELD);
+
+ case TRAN_STATUE:
+ return (use_which == EQ_WEAPON
+ || use_which == EQ_CLOAK
+ || use_which == EQ_HELMET);
+
+ case TRAN_ICE_BEAST:
+ return (use_which == EQ_CLOAK);
+
+ default:
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+void _extra_hp(int amount_extra) // must also set in calc_hp
+{
+ calc_hp();
+
+ you.hp *= amount_extra;
+ you.hp /= 10;
+
+ deflate_hp(you.hp_max, false);
+}
+
+// Used to mark transformations which override species/mutation intrinsics.
+// If phys_scales is true then we're checking to see if the form keeps
+// the physical (AC/EV) properties from scales... the special intrinsic
+// features (resistances, etc.) are lost in those forms however.
+bool transform_changed_physiology( bool phys_scales )
+{
+ return (you.attribute[ATTR_TRANSFORMATION] != TRAN_NONE
+ && you.attribute[ATTR_TRANSFORMATION] != TRAN_BLADE_HANDS
+ && (!phys_scales
+ || (you.attribute[ATTR_TRANSFORMATION] != TRAN_LICH
+ && you.attribute[ATTR_TRANSFORMATION] != TRAN_STATUE)));
+}