From d4c8c198886faba0f5b1f78063bfcff6a653af9e Mon Sep 17 00:00:00 2001
From: nagdon <m1nagdon@gmail.com>
Date: Wed, 13 Aug 2014 10:12:13 +0200
Subject: [PATCH] Clarify why teleportation/hasting is unavailable

In sprint mode, teleportations (but not blinks) are rejected with "Long-range
teleportation is disallowed in Dungeon Sprint." Formicids keep their short "You
cannot teleport/haste." messages (stasis is their main defining property, I
don't think that longer messages would be neccessary). If neither of these
permanent effects apply, the code will report all (temporary) effects currently
preventing teleportation (hasting is only blocked by amulet of stasis, so that
case is simpler). Theoretically this can create long messages (up to three
lines), but all teleport-blocking conditions are rare and these messages only
appear when the player tries something impossible, so this isn't a problem.

I duplicated some code from player::scan_artefacts() because I didn't want to
overcomplicate it with manipulation of lists of item names.

This was suggested in the following thread:
https://crawl.develz.org/tavern/viewtopic.php?f=8&t=12916
---
 crawl-ref/source/ability.cc  |    9 +--
 crawl-ref/source/item_use.cc |   28 ++++------
 crawl-ref/source/item_use.h  |    3 +-
 crawl-ref/source/player.cc   |  126 +++++++++++++++++++++++++++++++++++++++---
 crawl-ref/source/player.h    |    2 +
 crawl-ref/source/spl-cast.cc |   22 +++-----
 crawl-ref/source/spl-cast.h  |    2 +-
 7 files changed, 143 insertions(+), 49 deletions(-)

diff --git a/crawl-ref/source/ability.cc b/crawl-ref/source/ability.cc
index 4a52059..65cc6f1 100644
--- a/crawl-ref/source/ability.cc
+++ b/crawl-ref/source/ability.cc
@@ -1660,15 +1660,8 @@ static bool _check_ability_possible(const ability_def& abil,
 
     case ABIL_BLINK:
     case ABIL_EVOKE_BLINK:
-        if (you.no_tele(false, false, true))
+        if (you.no_tele_print_reason(false, false, true, quiet))
         {
-            if (!quiet)
-            {
-                if (you.species == SP_FORMICID)
-                    mpr("You cannot teleport.");
-                else
-                    mpr("You cannot teleport right now.");
-            }
             return false;
         }
         return true;
diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc
index 49b42d7..fb12097 100644
--- a/crawl-ref/source/item_use.cc
+++ b/crawl-ref/source/item_use.cc
@@ -1778,20 +1778,15 @@ void zap_wand(int slot)
 
     if (alreadyknown && zap_wand.target == you.pos())
     {
-        if (wand.sub_type == WAND_TELEPORTATION && you.no_tele(false, false))
+        if (wand.sub_type == WAND_TELEPORTATION
+            && you.no_tele_print_reason(false, false))
         {
-            if (you.species == SP_FORMICID)
-                mpr("You cannot teleport.");
-            else
-                mpr("You cannot teleport right now.");
             return;
         }
-        else if (wand.sub_type == WAND_HASTING && you.stasis(false))
+        else if (wand.sub_type == WAND_HASTING
+                 && stasis_blocks_effect(false, "%s prevents hasting.",
+                                         0, NULL, "You cannot haste."))
         {
-            if (you.species == SP_FORMICID)
-                mpr("You cannot haste.");
-            else
-                mpr("You cannot haste right now.");
             return;
         }
         else if (wand.sub_type == WAND_INVISIBILITY && _dont_use_invis())
@@ -2711,12 +2706,9 @@ void read_scroll(int slot)
         {
         case SCR_BLINKING:
         case SCR_TELEPORTATION:
-            if (you.no_tele(false, false, which_scroll == SCR_BLINKING))
+            if (you.no_tele_print_reason(false, false,
+                which_scroll == SCR_BLINKING))
             {
-                if (you.species == SP_FORMICID)
-                    mpr("You cannot teleport.");
-                else
-                    mpr("You cannot teleport right now.");
                 return;
             }
             break;
@@ -3060,7 +3052,8 @@ void read_scroll(int slot)
 
 bool stasis_blocks_effect(bool calc_unid,
                           const char *msg, int noise,
-                          const char *silenced_msg)
+                          const char *silenced_msg,
+                          const char *formicid_msg)
 {
     if (you.stasis(calc_unid))
     {
@@ -3074,7 +3067,8 @@ bool stasis_blocks_effect(bool calc_unid,
         {
             // Override message for formicids
             if (you.species == SP_FORMICID)
-                mpr("Your stasis keeps you stable.");
+                mpr(formicid_msg ? formicid_msg :
+                                   "Your stasis keeps you stable.");
             else
             {
                 const string name(amulet? amulet->name(DESC_YOUR) : "Something");
diff --git a/crawl-ref/source/item_use.h b/crawl-ref/source/item_use.h
index 2db835e..becc994 100644
--- a/crawl-ref/source/item_use.h
+++ b/crawl-ref/source/item_use.h
@@ -50,7 +50,8 @@ void prompt_inscribe_item();
 
 bool stasis_blocks_effect(bool calc_unid,
                           const char *msg, int noise = 0,
-                          const char *silencedmsg = NULL);
+                          const char *silenced_msg = NULL,
+                          const char *formicid_msg = NULL);
 
 #ifdef USE_TILE
 void tile_item_use_floor(int idx);
diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc
index a9fa783..07b79c2 100644
--- a/crawl-ref/source/player.cc
+++ b/crawl-ref/source/player.cc
@@ -6995,20 +6995,132 @@ int player_res_magic(bool calc_unid, bool temp)
     return rm;
 }
 
-bool player::no_tele(bool calc_unid, bool permit_id, bool blinking) const
+bool player::no_tele_print_reason(bool calc_unid, bool permit_id, bool blinking, bool quiet) const
 {
-    if (duration[DUR_DIMENSION_ANCHOR])
+    if (crawl_state.game_is_sprint() && !blinking)
+    {
+        if (!quiet)
+            mpr("Long-range teleportation is disallowed in Dungeon Sprint.");
         return true;
+    }
 
-    if (crawl_state.game_is_sprint() && !blinking)
+    if (you.species == SP_FORMICID)
+    {
+        if (!quiet)
+            mpr("You cannot teleport.");
         return true;
+    }
+
+    vector<string> problems;
+    bool found_any = false;
+
+    if (duration[DUR_DIMENSION_ANCHOR])
+    {
+        problems.push_back("locked down by Dimension Anchor");
+        found_any = true;
+    }
 
     if (form == TRAN_TREE)
-        return true;
+    {
+        problems.push_back("held in place by your roots");
+        found_any = true;
+    }
+
+    if (crawl_state.game_is_zotdef() && orb_haloed(pos()))
+    {
+        problems.push_back("in the halo of the Orb");
+        found_any = true;
+    }
+
+    //This duplicates some code from player::scan_artefacts()
+    bool stasis_block = false;
+    if (has_notele_item(calc_unid)
+        || (stasis_block = stasis_blocks_effect(calc_unid, NULL))) {
+        found_any = true;
+        if (!quiet) {
+            vector<string> worn_notele;
+            bool found_nonartefact = false;
+            bool found_stasis = false;
+
+            for (int i = EQ_WEAPON; i < NUM_EQUIP; ++i)
+            {
+                bool current_is_notele = false;
+
+                if (melded[i] || equip[i] == -1)
+                    continue;
+
+                const int eq = equip[i];
+
+                // Copied from scan_artefacts(), but probably unneccessary
+                // because armour and jewellery can't be wielded:
+                if (i == EQ_WEAPON && inv[eq].base_type != OBJ_WEAPONS)
+                    continue;
 
-    return has_notele_item(calc_unid)
-           || stasis_blocks_effect(calc_unid, NULL)
-           || crawl_state.game_is_zotdef() && orb_haloed(pos());
+                if (i == EQ_AMULET && inv[eq].sub_type == AMU_STASIS
+                    && (calc_unid || inv[eq].flags & ISFLAG_KNOW_TYPE))
+                {
+                    current_is_notele = true;
+                    found_stasis = true;
+                    found_nonartefact = !is_artefact(inv[eq]);
+                }
+
+                if (is_artefact(inv[eq]))
+                {
+                    bool known;
+                    int val = artefact_wpn_property(inv[eq],
+                                        ARTP_PREVENT_TELEPORTATION, known);
+                    if ((calc_unid || known) && val)
+                        current_is_notele = true;
+                }
+                
+                if (current_is_notele)
+                {
+                    if (i == EQ_WEAPON) {
+                        problems.push_back(make_stringf("wielding %s",
+                                   inv[eq].name(DESC_A).c_str()));
+                    }
+                    else
+                    {
+                        worn_notele.push_back(inv[eq].name(DESC_A));
+                    }
+                }
+            }
+
+            if (worn_notele.size()>(problems.empty() ? 3 : 1)) {
+                problems.push_back(
+                    make_stringf("wearing %s %s preventing teleportation",
+                                 number_in_words(worn_notele.size()).c_str(),
+                                 found_nonartefact ? "items": "artefacts"));
+            }
+            else if (!worn_notele.empty()) {
+                problems.push_back(
+                    make_stringf("wearing %s",
+                                 comma_separated_line(worn_notele.begin(),
+                                 worn_notele.end()).c_str()));
+            }
+
+            if (stasis_block && !found_stasis) {
+                //Formicids and AMU_STASIS are handled above, other sources
+                //of stasis will display this message:
+                problems.push_back("affected by a buggy stasis");
+            }
+        }
+    }
+
+    if (found_any && !quiet) {
+        if (problems.empty())
+            problems.push_back("affected by something buggy");
+
+        mprf("You cannot teleport because you are %s.", comma_separated_line(
+             problems.begin(), problems.end()).c_str());
+    }
+
+    return found_any;
+}
+
+bool player::no_tele(bool calc_unid, bool permit_id, bool blinking) const
+{
+    return no_tele_print_reason(calc_unid, permit_id, blinking, true);
 }
 
 bool player::fights_well_unarmed(int heavy_armour_penalty)
diff --git a/crawl-ref/source/player.h b/crawl-ref/source/player.h
index e2da239..70b7bde 100644
--- a/crawl-ref/source/player.h
+++ b/crawl-ref/source/player.h
@@ -646,6 +646,8 @@ public:
     int res_magic() const;
     bool no_tele(bool calc_unid = true, bool permit_id = true,
                  bool blink = false) const;
+    bool no_tele_print_reason(bool calc_unid = true, bool permit_id = true,
+                 bool blink = false, bool quiet = false) const;
 
     bool gourmand(bool calc_unid = true, bool items = true) const;
     bool res_corr(bool calc_unid = true, bool items = true) const;
diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc
index da34361..266e14a 100644
--- a/crawl-ref/source/spl-cast.cc
+++ b/crawl-ref/source/spl-cast.cc
@@ -29,6 +29,7 @@
 #include "godconduct.h"
 #include "goditem.h"
 #include "hints.h"
+#include "item_use.h"
 #include "libutil.h"
 #include "macro.h"
 #include "menu.h"
@@ -966,12 +967,12 @@ static bool _too_hot_to_cast(spell_type spell)
 }
 #endif
 
-bool is_prevented_teleport(spell_type spell)
+bool is_prevented_teleport(spell_type spell, bool quiet)
 {
     return (spell == SPELL_BLINK
              || spell == SPELL_CONTROLLED_BLINK
              || spell == SPELL_TELEPORT_SELF)
-            && you.no_tele(false, false, spell != SPELL_TELEPORT_SELF);
+            && you.no_tele_print_reason(false, false, spell != SPELL_TELEPORT_SELF, quiet);
 }
 
 bool spell_is_uncastable(spell_type spell, string &msg)
@@ -1088,14 +1089,8 @@ static bool _spellcasting_aborted(spell_type spell,
         return true;
     }
 
-    if (is_prevented_teleport(spell))
-    {
-        if (you.species == SP_FORMICID)
-            mpr("You cannot teleport.");
-        else
-            mpr("You cannot teleport right now.");
+    if (is_prevented_teleport(spell, false))
         return true;
-    }
 
     if (spell == SPELL_MALIGN_GATEWAY && !can_cast_malign_gateway())
     {
@@ -1328,13 +1323,10 @@ spret_type your_spells(spell_type spell, int powc,
             return SPRET_ABORT;
         }
 
-        if (spell == SPELL_HASTE && spd.isMe() && you.stasis(false))
+        if (spell == SPELL_HASTE && spd.isMe()
+            && stasis_blocks_effect(false, "%s prevents hasting.",
+                                    0, NULL, "You cannot haste."))
         {
-            if (you.species == SP_FORMICID)
-                mpr("You cannot haste.");
-            else
-                mpr("You cannot haste right now.");
-
             return SPRET_ABORT;
         }
     }
diff --git a/crawl-ref/source/spl-cast.h b/crawl-ref/source/spl-cast.h
index f590ebe..3c732d6 100644
--- a/crawl-ref/source/spl-cast.h
+++ b/crawl-ref/source/spl-cast.h
@@ -82,7 +82,7 @@ string spell_schools_string(spell_type spell);
 string spell_hunger_string(spell_type spell, bool rod = false);
 string spell_noise_string(spell_type spell);
 
-bool is_prevented_teleport(spell_type spell);
+bool is_prevented_teleport(spell_type spell, bool quiet = true);
 
 bool spell_is_uncastable(spell_type spell, string &message);
 void spell_skills(spell_type spell, set<skill_type> &skills);
-- 
1.7.10.4

