summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2006-12-30 13:07:34 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2006-12-30 13:07:34 +0000
commitc9a0cfa661cdad829f2e4af499670b95e3b14336 (patch)
tree772486cfecdd689ce5b824d4343b7e0c631ee43a
parent82c469a3a5b523c0b82229d289b122e3868324cc (diff)
downloadcrawl-ref-c9a0cfa661cdad829f2e4af499670b95e3b14336.tar.gz
crawl-ref-c9a0cfa661cdad829f2e4af499670b95e3b14336.zip
Backported greedy explore and travel stop messages for 0.1.7 (I'm akrasiac, so
shoot me). git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/branches/stone_soup-0.1.7@744 c06c8d41-db1a-0410-9941-cceddc491573
-rw-r--r--crawl-ref/source/acr.cc7
-rw-r--r--crawl-ref/source/delay.cc29
-rw-r--r--crawl-ref/source/enum.h9
-rw-r--r--crawl-ref/source/externs.h12
-rw-r--r--crawl-ref/source/initfile.cc112
-rw-r--r--crawl-ref/source/items.cc80
-rw-r--r--crawl-ref/source/items.h4
-rw-r--r--crawl-ref/source/libutil.h27
-rw-r--r--crawl-ref/source/misc.cc13
-rw-r--r--crawl-ref/source/misc.h1
-rw-r--r--crawl-ref/source/mon-util.cc2
-rw-r--r--crawl-ref/source/mon-util.h2
-rw-r--r--crawl-ref/source/stash.cc73
-rw-r--r--crawl-ref/source/stash.h14
-rw-r--r--crawl-ref/source/stuff.cc6
-rw-r--r--crawl-ref/source/stuff.h2
-rw-r--r--crawl-ref/source/travel.cc453
-rw-r--r--crawl-ref/source/travel.h54
-rw-r--r--crawl-ref/source/view.h2
19 files changed, 761 insertions, 141 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index ea154352dd..c31cc70e24 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -1269,7 +1269,7 @@ void process_command( command_type cmd ) {
break;
}
// Start exploring
- start_explore();
+ start_explore(Options.explore_greedy);
break;
case CMD_DISPLAY_MAP:
@@ -2516,12 +2516,15 @@ keycode_type get_next_keycode() {
return keyin;
}
-static void middle_input() {
+static void middle_input()
+{
+#ifdef STASH_TRACKING
if (Options.stash_tracking)
stashes.update_visible_stashes(
Options.stash_tracking == STM_ALL?
StashTracker::ST_AGGRESSIVE :
StashTracker::ST_PASSIVE);
+#endif
}
/*
diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc
index ae2c75a3aa..1f46c43541 100644
--- a/crawl-ref/source/delay.cc
+++ b/crawl-ref/source/delay.cc
@@ -28,6 +28,7 @@
#include "it_use2.h"
#include "message.h"
#include "misc.h"
+#include "mon-util.h"
#include "monstuff.h"
#include "mstuff2.h"
#include "ouch.h"
@@ -369,7 +370,14 @@ void handle_delay( void )
switch (delay.type)
{
case DELAY_AUTOPICKUP:
+ {
+ const int estop =
+ you.running == RMODE_EXPLORE_GREEDY?
+ ES_GREEDY_PICKUP : ES_PICKUP;
+ if ((Options.explore_stop & estop) && prompt_stop_explore(estop))
+ stop_delay();
break;
+ }
case DELAY_WEAPON_SWAP:
weapon_switch( delay.parm1 );
@@ -889,6 +897,25 @@ static bool should_stop_activity(const delay_queue_item &item,
(Options.activity_interrupts[item.type][ai]));
}
+inline static void monster_warning(activity_interrupt_type ai,
+ const activity_interrupt_data &at,
+ int atype)
+{
+ if ( ai == AI_SEE_MONSTER && is_run_delay(atype) )
+ {
+ const monsters* mon = static_cast<const monsters*>(at.data);
+#ifndef DEBUG_DIAGNOSTICS
+ mprf(MSGCH_WARN, "%s comes into view.", ptr_monam(mon, DESC_CAP_A));
+#else
+ mprf(MSGCH_WARN,
+ "%s in view: (%d,%d), see_grid: %s",
+ ptr_monam(mon, DESC_PLAIN),
+ mon->x, mon->y,
+ see_grid(mon->x, mon->y)? "yes" : "no");
+#endif
+ }
+}
+
void interrupt_activity( activity_interrupt_type ai,
const activity_interrupt_data &at )
{
@@ -901,6 +928,7 @@ void interrupt_activity( activity_interrupt_type ai,
if (should_stop_activity(item, ai, at))
{
+ monster_warning(ai, at, item.type);
stop_delay();
return;
}
@@ -920,6 +948,7 @@ void interrupt_activity( activity_interrupt_type ai,
{
if (is_run_delay( you.delay_queue[j].type ))
{
+ monster_warning(ai, at, you.delay_queue[j].type);
stop_delay();
return;
}
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 8b1caa093b..49256b7ffd 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -2843,10 +2843,11 @@ enum run_dir_type
enum run_mode_type
{
- RMODE_INTERLEVEL = -3, // Interlevel travel (Ctrl+G)
- RMODE_EXPLORE = -2, // Exploring (Ctrl+O)
- RMODE_TRAVEL = -1, // Classic or Plain Old travel
- RMODE_NOT_RUNNING = 0, // must remain equal to 0
+ RMODE_INTERLEVEL = -4, // Interlevel travel (Ctrl+G)
+ RMODE_EXPLORE_GREEDY = -3, // Explore + grab items (Tab/Ctrl+I)
+ RMODE_EXPLORE = -2, // Exploring (Ctrl+O)
+ RMODE_TRAVEL = -1, // Classic or Plain Old travel
+ RMODE_NOT_RUNNING = 0, // must remain equal to 0
RMODE_CONTINUE,
RMODE_START,
RMODE_REST_DURATION = 100
diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h
index 74c51e51aa..b4c499064e 100644
--- a/crawl-ref/source/externs.h
+++ b/crawl-ref/source/externs.h
@@ -305,6 +305,8 @@ public:
// Returns true if we're currently resting.
bool is_rest() const;
+ bool is_explore() const;
+
// Clears run state.
void clear();
@@ -839,6 +841,13 @@ public:
int explore_stop; // Stop exploring if a previously unseen
// item comes into view
+ int explore_stop_prompt;
+
+ bool explore_greedy; // Explore goes after items as well.
+
+ // How much more eager greedy-explore is for items than to explore.
+ int explore_item_greed;
+
std::vector<sound_mapping> sound_mappings;
std::vector<colour_mapping> menu_colour_mappings;
@@ -929,8 +938,9 @@ private:
void set_activity_interrupt(const std::string &activity_name,
const std::string &interrupt_names,
bool append_interrupts);
- void add_dump_fields(const std::string &text);
+ void new_dump_fields(const std::string &text, bool add = true);
void do_kill_map(const std::string &from, const std::string &to);
+ int read_explore_stop_conditions(const std::string &) const;
static const std::string interrupt_prefix;
};
diff --git a/crawl-ref/source/initfile.cc b/crawl-ref/source/initfile.cc
index 60fa8415f2..243c3a2544 100644
--- a/crawl-ref/source/initfile.cc
+++ b/crawl-ref/source/initfile.cc
@@ -372,10 +372,26 @@ static unsigned curses_attribute(const std::string &field)
return CHATTR_NORMAL;
}
-void game_options::add_dump_fields(const std::string &text)
+void game_options::new_dump_fields(const std::string &text, bool add)
{
// Easy; chardump.cc has most of the intelligence.
- append_vector(dump_order, split_string(",", text, true, true));
+ std::vector<std::string> fields = split_string(",", text, true, true);
+ if (add)
+ append_vector(dump_order, fields);
+ else
+ {
+ for (int f = 0, size = fields.size(); f < size; ++f)
+ {
+ for (int i = 0, dsize = dump_order.size(); i < dsize; ++i)
+ {
+ if (dump_order[i] == fields[f])
+ {
+ dump_order.erase( dump_order.begin() + i );
+ break;
+ }
+ }
+ }
+ }
}
void game_options::reset_startup_options()
@@ -599,7 +615,17 @@ void game_options::reset_options()
#ifdef STASH_TRACKING
stash_tracking = STM_ALL;
#endif
- explore_stop = ES_ITEM | ES_STAIR | ES_SHOP | ES_ALTAR;
+
+ explore_stop = ES_ITEM | ES_STAIR | ES_SHOP | ES_ALTAR
+ | ES_GREEDY_PICKUP;
+
+ // The prompt conditions will be combined into explore_stop after
+ // reading options.
+ explore_stop_prompt = ES_NONE;
+
+ explore_item_greed = 10;
+ explore_greedy = false;
+
safe_zero_exp = true;
target_zero_exp = false;
target_wrap = true;
@@ -670,7 +696,7 @@ void game_options::reset_options()
// Clear vector options.
dump_order.clear();
- add_dump_fields("header,stats,misc,inventory,skills,"
+ new_dump_fields("header,stats,misc,inventory,skills,"
"spells,mutations,messages,screenshot,kills,notes");
banned_objects.clear();
@@ -1002,6 +1028,8 @@ void game_options::read_options(InitLineInput &il, bool runscript)
}
#endif
+ Options.explore_stop |= Options.explore_stop_prompt;
+
// Validate save_dir
if (!check_dir("Save directory", save_dir))
exit(1);
@@ -1030,6 +1058,29 @@ void game_options::do_kill_map(const std::string &from, const std::string &to)
kill_map[ifrom] = ito;
}
+int game_options::read_explore_stop_conditions(const std::string &field) const
+{
+ int conditions = 0;
+ std::vector<std::string> stops = split_string(",", field);
+ for (int i = 0, count = stops.size(); i < count; ++i)
+ {
+ const std::string &c = stops[i];
+ if (c == "item" || c == "items")
+ conditions |= ES_ITEM;
+ else if (c == "pickup")
+ conditions |= ES_PICKUP;
+ else if (c == "greedy_pickup" || c == "greedy pickup")
+ conditions |= ES_GREEDY_PICKUP;
+ else if (c == "shop" || c == "shops")
+ conditions |= ES_SHOP;
+ else if (c == "stair" || c == "stairs")
+ conditions |= ES_STAIR;
+ else if (c == "altar" || c == "altars")
+ conditions |= ES_ALTAR;
+ }
+ return (conditions);
+}
+
void game_options::read_option_line(const std::string &str, bool runscript)
{
std::string key = "";
@@ -1040,6 +1091,7 @@ void game_options::read_option_line(const std::string &str, bool runscript)
int first_dot = str.find('.');
bool plus_equal = false;
+ bool minus_equal = false;
// all lines with no equal-signs we ignore
if (first_equals < 0)
@@ -1069,6 +1121,12 @@ void game_options::read_option_line(const std::string &str, bool runscript)
key = key.substr(0, key.length() - 1);
trim_string(key);
}
+ else if (key.length() && key[key.length() - 1] == '-')
+ {
+ minus_equal = true;
+ key = key.substr(0, key.length() - 1);
+ trim_string(key);
+ }
tolower_string( trim_string( subkey ) );
@@ -1767,20 +1825,36 @@ void game_options::read_option_line(const std::string &str, bool runscript)
}
else if (key == "explore_stop")
{
- explore_stop = ES_NONE;
- std::vector<std::string> stops = split_string(",", field);
- for (int i = 0, count = stops.size(); i < count; ++i)
- {
- const std::string &c = stops[i];
- if (c == "item" || c == "items")
- explore_stop |= ES_ITEM;
- else if (c == "shop" || c == "shops")
- explore_stop |= ES_SHOP;
- else if (c == "stair" || c == "stairs")
- explore_stop |= ES_STAIR;
- else if (c == "altar" || c == "altars")
- explore_stop |= ES_ALTAR;
- }
+ if (!plus_equal && !minus_equal)
+ explore_stop = ES_NONE;
+
+ const int new_conditions = read_explore_stop_conditions(field);
+ if (minus_equal)
+ explore_stop &= ~new_conditions;
+ else
+ explore_stop |= new_conditions;
+ }
+ else if (key == "explore_stop_prompt")
+ {
+ if (!plus_equal && !minus_equal)
+ explore_stop_prompt = ES_NONE;
+ const int new_conditions = read_explore_stop_conditions(field);
+ if (minus_equal)
+ explore_stop_prompt &= ~new_conditions;
+ else
+ explore_stop_prompt |= new_conditions;
+ }
+ else if (key == "explore_item_greed")
+ {
+ explore_item_greed = atoi( field.c_str() );
+ if (explore_item_greed > 1000)
+ explore_item_greed = 1000;
+ else if (explore_item_greed < -1000)
+ explore_item_greed = -1000;
+ }
+ else if (key == "explore_greedy")
+ {
+ explore_greedy = read_bool(field, explore_greedy);
}
#ifdef STASH_TRACKING
else if (key == "stash_tracking")
@@ -1831,7 +1905,7 @@ void game_options::read_option_line(const std::string &str, bool runscript)
if (!plus_equal)
dump_order.clear();
- add_dump_fields(field);
+ new_dump_fields(field, !minus_equal);
}
else if (key == "dump_kill_places")
{
diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc
index f7734997d9..a37e4a812a 100644
--- a/crawl-ref/source/items.cc
+++ b/crawl-ref/source/items.cc
@@ -59,7 +59,6 @@
#include "stash.h"
static void autopickup(void);
-static bool is_stackable_item( const item_def &item );
static bool invisible_to_player( const item_def& item );
static void item_list_on_square( std::vector<const item_def*>& items,
int obj, bool force_squelch = false );
@@ -1277,7 +1276,7 @@ void pickup()
}
} // end pickup()
-static bool is_stackable_item( const item_def &item )
+bool is_stackable_item( const item_def &item )
{
if (!is_valid_item( item ))
return (false);
@@ -1287,6 +1286,7 @@ static bool is_stackable_item( const item_def &item )
|| item.base_type == OBJ_SCROLLS
|| item.base_type == OBJ_POTIONS
|| item.base_type == OBJ_UNKNOWN_II
+ || item.base_type == OBJ_GOLD
|| (item.base_type == OBJ_MISCELLANY
&& item.sub_type == MISC_RUNE_OF_ZOT))
{
@@ -1306,6 +1306,9 @@ bool items_stack( const item_def &item1, const item_def &item2 )
if (item1.base_type != item2.base_type || item1.sub_type != item2.sub_type)
return (false);
+ if (item1.base_type == OBJ_GOLD)
+ return (true);
+
// These classes also require pluses and special
if (item1.base_type == OBJ_MISSILES
|| item1.base_type == OBJ_MISCELLANY) // only runes
@@ -2914,56 +2917,75 @@ static void autoinscribe_items()
}
}
-static void autopickup(void)
+bool item_needs_autopickup(const item_def &item)
{
- //David Loewenstern 6/99
- int result, o, next;
- bool did_pickup = false;
+ return (strstr(item.inscription.c_str(), "=g") != 0
+ || ((item.flags & ISFLAG_THROWN) && Options.pickup_thrown)
+ || (((Options.autopickups & (1L << item.base_type))
+#ifdef CLUA_BINDINGS
+ || clua.callbooleanfn(false, "ch_autopickup", "u", &item)
+#endif
+ )
+ && (Options.pickup_dropped || !(item.flags & ISFLAG_DROPPED))
+ && !is_banned(item)));
+}
- if (!Options.autopickup_on || Options.autopickups == 0L)
- return;
+bool can_autopickup()
+{
+ // [ds] Checking for autopickups == 0 is a bad idea because
+ // autopickup is still possible with inscriptions and
+ // pickup_thrown.
+ if (!Options.autopickup_on)
+ return (false);
- if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR
- && you.duration[DUR_TRANSFORMATION] > 0)
- {
- return;
- }
+ if (you.attribute[ATTR_TRANSFORMATION] == TRAN_AIR
+ && you.duration[DUR_TRANSFORMATION] > 0)
+ return (false);
if (player_is_levitating() && !wearing_amulet(AMU_CONTROLLED_FLIGHT))
- return;
+ return (false);
if ( Options.safe_autopickup && !i_feel_safe() )
- return;
+ return (false);
+
+ return (true);
+}
+
+static void autopickup(void)
+{
+ //David Loewenstern 6/99
+ int result, o, next;
+ bool did_pickup = false;
+ bool tried_pickup = false;
+
+ if (!can_autopickup())
+ return;
o = igrd[you.x_pos][you.y_pos];
+ int unthrown = 0;
while (o != NON_ITEM)
{
next = mitm[o].link;
- if (
- (strstr(mitm[o].inscription.c_str(), "=g") != 0) || (
-
- ((mitm[o].flags & ISFLAG_THROWN) && Options.pickup_thrown) ||
- ( (Options.autopickups & (1L << mitm[o].base_type)
-#ifdef CLUA_BINDINGS
- || clua.callbooleanfn(false, "ch_autopickup", "u", &mitm[o])
-#endif
- )
- && (Options.pickup_dropped || !(mitm[o].flags & ISFLAG_DROPPED))
- && !is_banned(mitm[o]))))
+ if (item_needs_autopickup(mitm[o]))
{
+ if (!(mitm[o].flags & ISFLAG_THROWN))
+ unthrown++;
+
mitm[o].flags &= ~(ISFLAG_THROWN | ISFLAG_DROPPED);
result = move_item_to_player( o, mitm[o].quantity);
if (result == 0)
{
+ tried_pickup = true;
mpr("You can't carry any more.");
break;
}
else if (result == -1)
{
+ tried_pickup = true;
mpr("Your pack is full.");
break;
}
@@ -2980,8 +3002,12 @@ static void autopickup(void)
if (did_pickup)
{
you.turn_is_over = true;
- start_delay( DELAY_AUTOPICKUP, 1 );
+ start_delay( DELAY_AUTOPICKUP, 1, unthrown );
}
+ // Greedy explore has no good way to deal with an item that we can't
+ // pick up, so the only thing to do is to stop.
+ else if (tried_pickup && you.running == RMODE_EXPLORE_GREEDY)
+ stop_delay();
}
int inv_count(void)
diff --git a/crawl-ref/source/items.h b/crawl-ref/source/items.h
index 53a779c2b9..96d676d5b7 100644
--- a/crawl-ref/source/items.h
+++ b/crawl-ref/source/items.h
@@ -28,6 +28,7 @@ void inc_mitm_item_quantity( int obj, int amount );
void move_item_to_grid( int *const obj, int x, int y );
void move_item_stack_to_grid( int x, int y, int targ_x, int targ_y );
int move_item_to_player( int obj, int quant_got, bool quiet = false );
+bool is_stackable_item( const item_def &item );
bool items_stack( const item_def &item1, const item_def &item2 );
void init_item( int item );
@@ -139,4 +140,7 @@ void origin_set_startequip(item_def &item);
void origin_set_unknown(item_def &item);
void origin_set_inventory( void (*oset)(item_def &item) );
+bool item_needs_autopickup(const item_def &);
+bool can_autopickup();
+
#endif
diff --git a/crawl-ref/source/libutil.h b/crawl-ref/source/libutil.h
index 0f232baa72..a334497107 100644
--- a/crawl-ref/source/libutil.h
+++ b/crawl-ref/source/libutil.h
@@ -15,6 +15,7 @@
#include "AppHdr.h"
#include "defines.h"
+#include <cctype>
#include <string>
#include <vector>
@@ -63,6 +64,32 @@ std::vector<std::string> split_string(
bool trim = true,
bool accept_empties = false);
+inline std::string lowercase_first(std::string s)
+{
+ if (s.length())
+ s[0] = tolower(s[0]);
+ return (s);
+}
+
+template <class Z>
+std::string comma_separated_line(Z start, Z end)
+{
+ std::string text;
+ for (Z i = start; i != end; ++i)
+ {
+ if (i != start)
+ {
+ if (i + 1 != end)
+ text += ", ";
+ else
+ text += " and ";
+ }
+
+ text += i->name;
+ }
+ return (text);
+}
+
#ifdef NEED_USLEEP
void usleep( unsigned long time );
#endif
diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc
index 33be41d5df..e6f79e97c5 100644
--- a/crawl-ref/source/misc.cc
+++ b/crawl-ref/source/misc.cc
@@ -50,6 +50,7 @@
#include "monstuff.h"
#include "notes.h"
#include "ouch.h"
+#include "overmap.h"
#include "player.h"
#include "shopping.h"
#include "skills.h"
@@ -172,7 +173,7 @@ bool grid_destroys_items( int grid )
return (grid == DNGN_LAVA || grid == DNGN_DEEP_WATER);
}
-// returns 0 is grid is not an altar, else it returns the GOD_* type
+// returns 0 if grid is not an altar, else it returns the GOD_* type
god_type grid_altar_god( unsigned char grid )
{
if (grid >= DNGN_ALTAR_ZIN && grid <= DNGN_ALTAR_ELYVILON)
@@ -181,6 +182,16 @@ god_type grid_altar_god( unsigned char grid )
return (GOD_NO_GOD);
}
+// returns DNGN_FLOOR for non-gods, otherwise returns the altar for
+// the god.
+int altar_for_god( god_type god )
+{
+ if (god == GOD_NO_GOD || god >= NUM_GODS)
+ return (DNGN_FLOOR); // Yeah, lame. Tell me about it.
+
+ return (DNGN_ALTAR_ZIN + god - 1);
+}
+
bool grid_is_branch_stairs( unsigned char grid )
{
return ((grid >= DNGN_ENTER_ORCISH_MINES && grid <= DNGN_ENTER_RESERVED_4)
diff --git a/crawl-ref/source/misc.h b/crawl-ref/source/misc.h
index 9f6252fd70..b19610fefd 100644
--- a/crawl-ref/source/misc.h
+++ b/crawl-ref/source/misc.h
@@ -134,6 +134,7 @@ bool grid_is_solid(int grid);
bool grid_is_water(int grid);
bool grid_is_watery( int grid );
god_type grid_altar_god( unsigned char grid );
+int altar_for_god( god_type god );
bool grid_is_branch_stairs( unsigned char grid );
int grid_secret_door_appearance( int gx, int gy );
bool grid_destroys_items( int grid );
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 111718ec71..76f04faca7 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -1324,7 +1324,7 @@ void define_monster(int index)
/* ------------------------- monam/moname ------------------------- */
-const char *ptr_monam( struct monsters *mon, char desc )
+const char *ptr_monam( const monsters *mon, char desc )
{
// We give an item type description for mimics now, note that
// since gold mimics only have one description (to match the
diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h
index 3ad17c8f11..133df730b2 100644
--- a/crawl-ref/source/mon-util.h
+++ b/crawl-ref/source/mon-util.h
@@ -133,7 +133,7 @@ void init_monsters( FixedVector<unsigned short, 1000>& colour );
const char *monam(int mons_num, int mons, bool vis, char desc, int mons_wpn = NON_ITEM);
// these front for monam
-const char *ptr_monam( struct monsters *mon, char desc );
+const char *ptr_monam( const monsters *mon, char desc );
// last updated 12may2000 {dlb}
diff --git a/crawl-ref/source/stash.cc b/crawl-ref/source/stash.cc
index b1b027fb3a..d614739b6a 100644
--- a/crawl-ref/source/stash.cc
+++ b/crawl-ref/source/stash.cc
@@ -163,14 +163,22 @@ Stash::Stash(int xp, int yp) : enabled(true), items()
bool Stash::are_items_same(const item_def &a, const item_def &b)
{
- return a.base_type == b.base_type &&
- a.sub_type == b.sub_type &&
- a.plus == b.plus &&
- a.plus2 == b.plus2 &&
- a.special == b.special &&
- a.colour == b.colour &&
- a.flags == b.flags &&
- a.quantity == b.quantity;
+ const bool same = a.base_type == b.base_type
+ && a.sub_type == b.sub_type
+ && a.plus == b.plus
+ && a.plus2 == b.plus2
+ && a.special == b.special
+ && a.colour == b.colour
+ && a.flags == b.flags
+ && a.quantity == b.quantity;
+
+ // Account for rotting meat when comparing items.
+ return (same
+ || (a.base_type == b.base_type
+ && (a.base_type == OBJ_CORPSES
+ || (a.base_type == OBJ_FOOD && a.sub_type == FOOD_CHUNK
+ && b.sub_type == FOOD_CHUNK))
+ && a.plus == b.plus));
}
void Stash::filter(const std::string &str)
@@ -216,6 +224,19 @@ bool Stash::is_filtered(const item_def &item)
return false;
}
+bool Stash::unverified() const
+{
+ return (!verified);
+}
+
+bool Stash::pickup_eligible() const
+{
+ for (int i = 0, size = items.size(); i < size; ++i)
+ if (item_needs_autopickup(items[i]))
+ return (true);
+ return (false);
+}
+
void Stash::update()
{
int objl = igrd[x][y];
@@ -247,9 +268,10 @@ void Stash::update()
}
// There's something on this square. Take a squint at it.
- item_def &item = mitm[objl];
+ const item_def &item = mitm[objl];
- if (item.link == NON_ITEM) items.clear();
+ if (item.link == NON_ITEM)
+ items.clear();
// We knew of nothing on this square, so we'll assume this is the
// only item here, but mark it as unverified unless we can see nothing
@@ -268,7 +290,7 @@ void Stash::update()
if (is_filtered(item)) return;
- item_def &first = items[0];
+ const item_def &first = items[0];
// Compare these items
if (!are_items_same(first, item))
{
@@ -326,6 +348,7 @@ class StashMenu : public InvMenu
{
public:
StashMenu() : InvMenu(MF_SINGLESELECT), can_travel(false) { }
+ unsigned char getkey() const;
public:
bool can_travel;
protected:
@@ -360,6 +383,11 @@ bool StashMenu::process_key(int key)
return Menu::process_key(key);
}
+unsigned char StashMenu::getkey() const
+{
+ return (lastch);
+}
+
static MenuEntry *stash_menu_fixup(MenuEntry *me)
{
const item_def *item = static_cast<const item_def *>( me->data );
@@ -841,13 +869,19 @@ bool LevelStashes::isBelowPlayer() const
Stash *LevelStashes::find_stash(int x, int y)
{
+ return const_cast<Stash *>(
+ const_cast<const LevelStashes *>(this)->find_stash(x, y) );
+}
+
+const Stash *LevelStashes::find_stash(int x, int y) const
+{
if (x == -1 || y == -1)
{
x = you.x_pos;
y = you.y_pos;
}
const int abspos = (GXM * y) + x;
- c_stashes::iterator st = stashes.find(abspos);
+ c_stashes::const_iterator st = stashes.find(abspos);
return (st == stashes.end()? NULL : &st->second);
}
@@ -861,6 +895,21 @@ const ShopInfo *LevelStashes::find_shop(int x, int y) const
return (NULL);
}
+bool LevelStashes::shop_needs_visit(int x, int y) const
+{
+ const ShopInfo *shop = find_shop(x, y);
+ return (shop && !shop->is_visited());
+}
+
+bool LevelStashes::needs_visit(int x, int y) const
+{
+ const Stash *s = find_stash(x, y);
+ if (s && (s->unverified() || s->pickup_eligible()))
+ return (true);
+
+ return (shop_needs_visit(x, y));
+}
+
ShopInfo &LevelStashes::get_shop(int x, int y)
{
for (unsigned i = 0; i < shops.size(); ++i)
diff --git a/crawl-ref/source/stash.h b/crawl-ref/source/stash.h
index 727fb656e4..2c454e8e9e 100644
--- a/crawl-ref/source/stash.h
+++ b/crawl-ref/source/stash.h
@@ -48,6 +48,14 @@ public:
bool show_menu(const std::string &place, bool can_travel) const;
+ // Returns true if this Stash contains items that are eligible for
+ // autopickup.
+ bool pickup_eligible() const;
+
+ // Returns true if this Stash is unverified (a visit by the character will
+ // verify the stash).
+ bool unverified() const;
+
bool matches_search(const std::string &prefix,
const base_pattern &search,
stash_search_result &res)
@@ -197,6 +205,7 @@ public:
LevelStashes();
Stash *find_stash(int x = -1, int y = -1);
+ const Stash *find_stash(int x = -1, int y = -1) const;
ShopInfo &get_shop(int x, int y);
const ShopInfo *find_shop(int x, int y) const;
@@ -207,6 +216,11 @@ public:
// location if no parameters are supplied.
bool update_stash(int x = -1, int y = -1);
+ // Returns true if the square at (x,y) contains potentially interesting
+ // swag that merits a personal visit (for EXPLORE_GREEDY).
+ bool needs_visit(int x, int y) const;
+ bool shop_needs_visit(int x, int y) const;
+
// Add stash at (x,y), or player's current location if no parameters are
// supplied
void add_stash(int x = -1, int y = -1);
diff --git a/crawl-ref/source/stuff.cc b/crawl-ref/source/stuff.cc
index 255ac335e6..0c2c61303d 100644
--- a/crawl-ref/source/stuff.cc
+++ b/crawl-ref/source/stuff.cc
@@ -699,11 +699,13 @@ void canned_msg(unsigned char which_message)
// jmf: general helper (should be used all over in code)
// -- idea borrowed from Nethack
-bool yesno( const char *str, bool safe, int safeanswer, bool clear_after )
+bool yesno( const char *str, bool safe, int safeanswer, bool clear_after,
+ bool interrupt_delays )
{
unsigned char tmp;
- interrupt_activity( AI_FORCE_INTERRUPT );
+ if (interrupt_delays)
+ interrupt_activity( AI_FORCE_INTERRUPT );
for (;;)
{
mpr(str, MSGCH_PROMPT);
diff --git a/crawl-ref/source/stuff.h b/crawl-ref/source/stuff.h
index c05231ceb5..19b0fd8517 100644
--- a/crawl-ref/source/stuff.h
+++ b/crawl-ref/source/stuff.h
@@ -60,7 +60,7 @@ void redraw_screen(void);
void canned_msg(unsigned char which_message);
bool yesno( const char * str, bool safe = true, int safeanswer = 0,
- bool clear_after = true );
+ bool clear_after = true, bool interrupt_delays = true );
int yesnoquit( const char* str, bool safe = true,
int safeanswer = 0, bool clear_after = true );
diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc
index 0796d42c3c..af363e4112 100644
--- a/crawl-ref/source/travel.cc
+++ b/crawl-ref/source/travel.cc
@@ -15,6 +15,9 @@
#include "clua.h"
#include "delay.h"
#include "describe.h"
+#include "direct.h"
+#include "itemname.h"
+#include "items.h"
#include "misc.h"
#include "mon-util.h"
#include "player.h"
@@ -250,13 +253,33 @@ static bool is_exclude_root(int x, int y)
return false;
}
+// Determines if the level is fully explored. Clobbers you.run_x/y.
+static bool fully_explored()
+{
+ const int oldrun = you.running;
+
+ if (!you.running.is_explore())
+ you.running = RMODE_EXPLORE;
+
+ // Do a second floodfill to check if the level is fully explored.
+ // Note we're passing in a features vector to force find_travel_pos to
+ // reseed past traps/deep water/lava. Icky.
+
+ std::vector<coord_def> features_dummy;
+ find_travel_pos(you.x_pos, you.y_pos, NULL, NULL, &features_dummy);
+ you.running = oldrun;
+
+ return !(you.running.x > 0 && you.running.y > 0);
+}
+
const char *run_mode_name(int runmode)
{
- return runmode == RMODE_TRAVEL? "travel" :
- runmode == RMODE_INTERLEVEL? "intertravel" :
- runmode == RMODE_EXPLORE? "explore" :
- runmode > 0? "run" :
- "";
+ return runmode == RMODE_TRAVEL? "travel" :
+ runmode == RMODE_INTERLEVEL? "intertravel" :
+ runmode == RMODE_EXPLORE? "explore" :
+ runmode == RMODE_EXPLORE_GREEDY? "explore_greedy" :
+ runmode > 0? "run" :
+ "";
}
unsigned char is_waypoint(int x, int y)
@@ -268,11 +291,11 @@ unsigned char is_waypoint(int x, int y)
}
#ifdef STASH_TRACKING
-inline bool is_stash(LevelStashes *ls, int x, int y)
+inline bool is_stash(const LevelStashes *ls, int x, int y)
{
if (!ls)
return (false);
- Stash *s = ls->find_stash(x, y);
+ const Stash *s = ls->find_stash(x, y);
return s && s->enabled;
}
#endif
@@ -364,18 +387,20 @@ static bool is_reseedable(int x, int y)
*/
static bool is_travel_ok(int x, int y, bool ignore_hostile)
{
- unsigned char grid = grd[x][y];
+ const int grid = grd[x][y];
- unsigned char envc = (unsigned char) env.map[x - 1][y - 1];
- if (!envc) return false;
+ if (!is_terrain_known(x, y))
+ return (false);
// Special-case secret doors so that we don't run into awkwardness when
// a monster opens a secret door without the hero seeing it, but the travel
// code paths through the secret door because it looks at the actual grid,
- // rather than the env overmap. Hopefully there won't be any more such
- // cases.
- // FIXME: is_terrain_changed ought to do this with the view.cc changes.
- if (envc == get_sightmap_char(DNGN_SECRET_DOOR)) return false;
+ // rather than the env overmap.
+ if ((grid == DNGN_OPEN_DOOR || grid == DNGN_CLOSED_DOOR)
+ && is_terrain_changed(x, y))
+ {
+ return (false);
+ }
unsigned char mon = mgrd[x][y];
if (mon != NON_MONSTER)
@@ -387,8 +412,8 @@ static bool is_travel_ok(int x, int y, bool ignore_hostile)
// Arguably the utility of this feature is greater than
// the information we're giving the player for free.
// Navigate around plants and fungi. Yet another tasty hack.
- if (player_monster_visible(&menv[mon]) &&
- mons_class_flag( menv[mon].type, M_NO_EXP_GAIN ))
+ if (player_monster_visible(&menv[mon])
+ && mons_class_flag( menv[mon].type, M_NO_EXP_GAIN ))
{
extern short point_distance[GXM][GYM];
@@ -443,12 +468,12 @@ static bool is_safe(int x, int y)
// b) Unfriendly, in which case we're in deep trouble, since travel
// should have been aborted already by the checks in view.cc.
}
- const char cloud = env.cgrid[x][y];
+ const int cloud = env.cgrid[x][y];
if (cloud == EMPTY_CLOUD)
return true;
// We can also safely run through smoke.
- const char cloud_type = env.cloud[ cloud ].type;
+ const int cloud_type = env.cloud[ cloud ].type;
return cloud_type == CLOUD_GREY_SMOKE ||
cloud_type == CLOUD_GREY_SMOKE_MON ||
cloud_type == CLOUD_BLUE_SMOKE ||
@@ -693,25 +718,33 @@ bool is_travelable_stair(unsigned gridc)
}
}
+// Prompts the user to stop explore if necessary for the given
+// explore-stop condition, returns true if explore should be stopped.
+bool prompt_stop_explore(int es_why)
+{
+ return (!(Options.explore_stop_prompt & es_why)
+ || yesno("Stop exploring?", true, 'n', true, false));
+}
+
#define ES_item (Options.explore_stop & ES_ITEM)
#define ES_shop (Options.explore_stop & ES_SHOP)
#define ES_stair (Options.explore_stop & ES_STAIR)
#define ES_altar (Options.explore_stop & ES_ALTAR)
/*
- * Given a square that has just become visible during explore, returns true
- * if the player might consider the square worth stopping explore for.
+ * Adds interesting stuf on (x, y) to explore_discoveries.
+ *
+ * NOTE: These are env.map coords, add +1 to get grid coords.
*/
-static bool is_interesting_square(int x, int y)
+inline static void check_interesting_square(int x, int y,
+ explore_discoveries &ed)
{
- if (ES_item && igrd[x + 1][y + 1] != NON_ITEM)
- return true;
+ coord_def pos = { x + 1, y + 1 };
+
+ if (ES_item && igrd[pos.x][pos.y] != NON_ITEM)
+ ed.found_item( pos, mitm[ igrd[pos.x][pos.y] ] );
- unsigned char grid = grd[x + 1][y + 1];
- return (ES_shop && grid == DNGN_ENTER_SHOP)
- || (ES_stair && is_stair(grid))
- || (ES_altar && is_altar(grid)
- && you.where_are_you != BRANCH_ECUMENICAL_TEMPLE);
+ ed.found_feature( pos, grd[pos.x][pos.y] );
}
static void userdef_run_stoprunning_hook(void)
@@ -739,9 +772,7 @@ void start_running(void)
{
userdef_run_startrunning_hook();
- if (you.running == RMODE_TRAVEL
- || you.running == RMODE_EXPLORE
- || you.running == RMODE_INTERLEVEL)
+ if (you.running < 0)
start_delay( DELAY_TRAVEL, 1 );
}
@@ -753,6 +784,35 @@ void stop_running(void)
you.running.stop();
}
+static bool is_valid_explore_target(int x, int y)
+{
+ // If an adjacent square is unmapped, it's valid.
+ for (int yi = -1; yi <= 1; ++yi)
+ {
+ for (int xi = -1; xi <= 1; ++xi)
+ {
+ if (!xi && !yi)
+ continue;
+
+ const int ax = x + xi, ay = y + yi;
+ if (!in_bounds(ax, ay))
+ continue;
+ if (!is_player_mapped( get_envmap_char(ax, ay) ))
+ return (true);
+ }
+ }
+
+#ifdef STASH_TRACKING
+ if (you.running == RMODE_EXPLORE_GREEDY)
+ {
+ LevelStashes *lev = stashes.find_current_level();
+ return (lev && lev->needs_visit(x, y));
+ }
+#endif
+
+ return (false);
+}
+
/*
* Top-level travel control (called from input() in acr.cc).
*
@@ -778,37 +838,67 @@ command_type travel()
return CMD_NO_CMD;
}
- if (Options.explore_stop && you.running == RMODE_EXPLORE)
+ if (you.running.is_explore())
{
// Scan through the shadow map, compare it with the actual map, and if
// there are any squares of the shadow map that have just been
// discovered and contain an item, or have an interesting dungeon
// feature, stop exploring.
+
+ explore_discoveries discoveries;
for (int y = 0; y < GYM - 1; ++y)
{
for (int x = 0; x < GXM - 1; ++x)
{
if (!is_player_mapped(mapshadow[x][y])
- && is_player_mapped((unsigned char) env.map[x][y])
- && is_interesting_square(x, y))
+ && is_player_mapped((unsigned char) env.map[x][y]))
{
- stop_running();
- y = GYM;
- break;
+ check_interesting_square(x, y, discoveries);
}
}
}
+
+ if (discoveries.prompt_stop())
+ stop_running();
+
copy(env.map, mapshadow);
}
- if (you.running == RMODE_EXPLORE)
+ if (you.running.is_explore())
{
+#ifdef STASH_TRACKING
// Exploring
- you.running.x = 0;
- find_travel_pos(you.x_pos, you.y_pos, NULL, NULL);
- // No place to go?
- if (!you.running.x)
- stop_running();
+ if (grd[you.x_pos][you.y_pos] == DNGN_ENTER_SHOP
+ && you.running == RMODE_EXPLORE_GREEDY)
+ {
+ LevelStashes *lev = stashes.find_current_level();
+ if (lev && lev->shop_needs_visit(you.x_pos, you.y_pos))
+ {
+ you.running = 0;
+ return (CMD_GO_UPSTAIRS);
+ }
+ }
+#endif
+
+ // Speed up explore by not doing a double-floodfill if we have
+ // a valid target.
+ if (!you.running.x
+ || (you.running.x == you.x_pos && you.running.y == you.y_pos)
+ || !is_valid_explore_target(you.running.x, you.running.y))
+ {
+ you.running.x = 0;
+ find_travel_pos(you.x_pos, you.y_pos, NULL, NULL);
+ // No place to go?
+ if (!you.running.x)
+ {
+ // Do fully_explored() *before* stop_running!
+ if (fully_explored())
+ mpr("Done exploring.");
+ else
+ mpr("Partly explored, some areas are inaccessible.");
+ stop_running();
+ }
+ }
}
if (you.running == RMODE_INTERLEVEL && !you.running.x)
@@ -830,6 +920,31 @@ command_type travel()
// we turn off travel (find_travel_pos does that automatically).
find_travel_pos(you.x_pos, you.y_pos, move_x, move_y);
+#ifdef STASH_TRACKING
+ if ((*move_x || *move_y) && you.running == RMODE_EXPLORE_GREEDY)
+ {
+ // Greedy explore should cut off on reaching an item. We can't
+ // check after reaching the item, because at that point the stash
+ // tracker will have verified the stash and say "false" to
+ // needs_visit.
+ const int new_x = you.x_pos + *move_x;
+ const int new_y = you.y_pos + *move_y;
+
+ if (new_x == you.running.x && new_y == you.running.y)
+ {
+ const LevelStashes *lev = stashes.find_current_level();
+ if (lev && lev->needs_visit(new_x, new_y)
+ && !lev->shop_needs_visit(new_x, new_y))
+ {
+ if ((Options.explore_stop & ES_ITEM)
+ && prompt_stop_explore(ES_ITEM))
+ stop_running();
+ return direction_to_command( *move_x, *move_y );
+ }
+ }
+ }
+#endif
+
if (!*move_x && !*move_y)
{
// If we've reached the square we were traveling towards, travel
@@ -839,8 +954,8 @@ command_type travel()
// our travel target, we're on a staircase and should take it.
if (you.x_pos == you.running.x && you.y_pos == you.running.y)
{
- if (runmode == RMODE_EXPLORE)
- you.running = RMODE_EXPLORE; // Turn explore back on
+ if (runmode == RMODE_EXPLORE || runmode == RMODE_EXPLORE_GREEDY)
+ you.running = runmode; // Turn explore back on
// For interlevel travel, we'll want to take the stairs unless
// the interlevel travel specified a destination square and
@@ -900,10 +1015,10 @@ command_type travel()
redraw_screen();
if (!you.running)
- return CMD_NO_CMD;
+ return CMD_NO_CMD;
if ( result != CMD_NO_CMD )
- return result;
+ return result;
return direction_to_command( *move_x, *move_y );
}
@@ -913,7 +1028,9 @@ command_type direction_to_command( char x, char y ) {
if ( x == -1 && y == 0 ) return CMD_MOVE_LEFT;
if ( x == -1 && y == 1 ) return CMD_MOVE_DOWN_LEFT;
if ( x == 0 && y == -1 ) return CMD_MOVE_UP;
- if ( x == 0 && y == 0 ) return CMD_NO_CMD;
+ if ( x == 0 && y == 0 )
+ return you.running == RMODE_EXPLORE_GREEDY?
+ CMD_INSPECT_FLOOR : CMD_NO_CMD;
if ( x == 0 && y == 1 ) return CMD_MOVE_DOWN;
if ( x == 1 && y == -1 ) return CMD_MOVE_UP_RIGHT;
if ( x == 1 && y == 0 ) return CMD_MOVE_RIGHT;
@@ -945,6 +1062,13 @@ static void fill_exclude_radius(const coord_def &c)
}
}
+#ifdef STASH_TRACKING
+static bool is_greed_inducing_square(const LevelStashes *ls, int x, int y)
+{
+ return (ls && ls->needs_visit(x, y));
+}
+#endif
+
/*
* The travel algorithm is based on the NetHack travel code written by Warwick
* Allison - used with his permission.
@@ -960,9 +1084,20 @@ void find_travel_pos(int youx, int youy,
bool floodout = false;
unsigned char feature;
#ifdef STASH_TRACKING
- LevelStashes *lev = features? stashes.find_current_level() : NULL;
+ const LevelStashes *lev = stashes.find_current_level();
+ const bool need_for_greed =
+ you.running == RMODE_EXPLORE_GREEDY && can_autopickup();
+#else
+ const bool need_for_greed = false;
#endif
+ // For greedy explore, keep track of the closest unexplored
+ // territory and the closest greedy square.
+ int e_x = 0, e_y = 0; // Unexplored
+ int i_x = 0, i_y = 0; // Square with interesting items
+ // Use these weird defaults to handle negative item greeds.
+ int ex_dist = -10000, ix_dist = -10000;
+
// Normally we start from the destination and floodfill outwards, looking
// for the character's current position. If we're merely trying to populate
// the point_distance array (or exploring), we'll want to start from the
@@ -1063,20 +1198,77 @@ void find_travel_pos(int youx, int youy,
for (int dir = 0; dir < 8; (dir += 2) == 8 && (dir = 1))
{
int dx = x + Compass[dir].x, dy = y + Compass[dir].y;
-
- if (dx <= 0 || dx >= GXM || dy <= 0 || dy >= GYM) continue;
+
+ if (!in_bounds(dx, dy))
+ continue;
unsigned char envf = env.map[dx - 1][dy - 1];
- if (floodout && you.running == RMODE_EXPLORE
- && !is_player_mapped(envf))
+ if (floodout && you.running.is_explore())
{
- // Setting running.x and running.y here is evil - this
- // function should ideally not modify game state in any way.
- you.running.x = x;
- you.running.y = y;
+ if (!is_player_mapped(envf))
+ {
+ if (!need_for_greed)
+ {
+ you.running.x = x;
+ you.running.y = y;
+ return;
+ }
+
+ if (ex_dist == -10000)
+ {
+ e_x = x;
+ e_y = y;
+ ex_dist =
+ traveled_distance + Options.explore_item_greed;
+ }
+ }
+#ifdef STASH_TRACKING
+ else if (need_for_greed
+ && ix_dist == -10000
+ && is_greed_inducing_square(lev, dx, dy)
+ && is_travel_ok(dx, dy, ignore_hostile))
+ {
+ i_x = dx;
+ i_y = dy;
+ ix_dist = traveled_distance + 1;
+ }
+#endif
- return;
+ // Short-circuit if we can.
+ if (need_for_greed)
+ {
+ const int refdist =
+ Options.explore_item_greed > 0? ex_dist: ix_dist;
+
+ if (refdist != -10000
+ && traveled_distance > refdist)
+ {
+ if (Options.explore_item_greed > 0)
+ ix_dist = 10000;
+ else
+ ex_dist = 10000;
+ }
+ }
+
+ // ex_dist/ix_dist are only ever set in
+ // greedy-explore so this check implies
+ // greedy-explore.
+ if (ex_dist != -10000 && ix_dist != -10000)
+ {
+ if (ex_dist < ix_dist)
+ {
+ you.running.x = e_x;
+ you.running.y = e_y;
+ }
+ else
+ {
+ you.running.x = i_x;
+ you.running.y = i_y;
+ }
+ return;
+ }
+
}
if ((dx != dest_x || dy != dest_y)
@@ -1188,6 +1380,18 @@ void find_travel_pos(int youx, int youy,
fill_exclude_radius(exc);
}
}
+
+ if (need_for_greed && (ex_dist != -10000 || ix_dist != -10000))
+ {
+ if (ix_dist != -10000)
+ {
+ e_x = i_x;
+ e_y = i_y;
+ }
+
+ you.running.x = e_x;
+ you.running.y = e_y;
+ }
}
// Mappings of which branches spring from which other branches, essential to
@@ -2159,14 +2363,25 @@ void start_travel(int x, int y)
start_running();
}
-void start_explore()
+void start_explore(bool grab_items)
{
- you.running = RMODE_EXPLORE;
- if (Options.explore_stop)
+ you.running = grab_items? RMODE_EXPLORE_GREEDY : RMODE_EXPLORE;
+ if (you.running == RMODE_EXPLORE_GREEDY
+#ifdef STASH_TRACKING
+ && Options.stash_tracking != STM_ALL
+#endif
+ )
{
- // Clone shadow array off map
- copy(env.map, mapshadow);
+ Options.explore_greedy = false;
+ mpr("Greedy explore is available only if stash_tracking = all");
+ more();
+ you.running = RMODE_EXPLORE;
}
+
+ // Clone shadow array off map
+ copy(env.map, mapshadow);
+
+ you.running.x = you.running.y = 0;
start_running();
}
@@ -3112,6 +3327,11 @@ bool runrest::is_rest() const
return (runmode > 0 && !x && !y);
}
+bool runrest::is_explore() const
+{
+ return (runmode == RMODE_EXPLORE || runmode == RMODE_EXPLORE_GREEDY);
+}
+
void runrest::rundown()
{
rest();
@@ -3143,3 +3363,110 @@ void runrest::check_mp()
&& you.magic_points > mp)
stop();
}
+
+/////////////////////////////////////////////////////////////////////////////
+// explore_discoveries
+
+explore_discoveries::explore_discoveries()
+ : es_flags(0), current_level(NULL), items(), stairs(), shops(), altars()
+{
+}
+
+std::string explore_discoveries::cleaned_feature_description(int grid) const
+{
+ std::string s = lowercase_first(feature_description(grid));
+ if (s.length() && s[s.length() - 1] == '.')
+ {
+ s.erase(s.length() - 1);
+ }
+ return (s);
+}
+
+void explore_discoveries::found_feature(const coord_def &pos, int grid)
+{
+ if (grid == DNGN_ENTER_SHOP && ES_shop)
+ {
+ shops.push_back( named_thing<int>( shop_name(pos.x, pos.y), grid ) );
+ es_flags |= ES_SHOP;
+ }
+ else if (is_stair(grid) && ES_stair)
+ {
+ stairs.push_back(
+ named_thing<int>(
+ cleaned_feature_description(grid), grid ) );
+ es_flags |= ES_STAIR;
+ }
+ else if (is_altar(grid) && ES_altar)
+ {
+ altars.push_back(
+ named_thing<int>(
+ cleaned_feature_description(grid), grid ) );
+ es_flags |= ES_ALTAR;
+ }
+}
+
+void explore_discoveries::add_item(const item_def &i)
+{
+ if (is_stackable_item(i))
+ {
+ // Try to find something to stack it with.
+ for (int j = 0, size = items.size(); j < size; ++j)
+ {
+ if (items_stack(i, items[j].thing))
+ {
+ items[j].thing.quantity += i.quantity;
+ items[j].name = item_name(items[j].thing, DESC_NOCAP_A);
+ return;
+ }
+ }
+ }
+
+ items.push_back( named_thing<item_def>(item_name(i, DESC_NOCAP_A), i) );
+}
+
+void explore_discoveries::found_item(const coord_def &pos, const item_def &i)
+{
+#ifdef STASH_TRACKING
+ if (you.running == RMODE_EXPLORE_GREEDY)
+ {
+ // The things we need to do...
+ if (!current_level)
+ current_level = stashes.find_current_level();
+
+ if (current_level && current_level->needs_visit(pos.x, pos.y))
+ return;
+ }
+#endif
+
+ add_item(i);
+ es_flags |= ES_ITEM;
+}
+
+template <class C> void explore_discoveries::say_any(
+ const C &coll, const char *stub) const
+{
+ if (coll.empty())
+ return;
+
+ const std::string message = "Found " +
+ comma_separated_line(coll.begin(), coll.end()) + ".";
+
+ if ((int) message.length() >= get_number_of_cols())
+ mprf(stub, coll.size());
+ else
+ mprf("%s", message.c_str());
+}
+
+bool explore_discoveries::prompt_stop() const
+{
+ if (!es_flags)
+ return (false);
+
+ say_any(items, "Found %u items.");
+ say_any(stairs, "Found %u stairs.");
+ say_any(altars, "Found %u altars.");
+ say_any(shops, "Found %u shops.");
+
+ return ((Options.explore_stop_prompt & es_flags) != es_flags
+ || prompt_stop_explore(es_flags));
+}
diff --git a/crawl-ref/source/travel.h b/crawl-ref/source/travel.h
index e53aaa8dac..cf106d2262 100644
--- a/crawl-ref/source/travel.h
+++ b/crawl-ref/source/travel.h
@@ -46,11 +46,14 @@ void find_travel_pos(int you_x, int you_y, char *move_x, char *move_y,
* Initiates explore - the character runs around the level to map it. Note
* that the caller has to ensure that the level is mappable before calling
* start_explore. start_explore may lock up the game on unmappable levels.
+ * If grab_items is true, greedy explore is triggered - in greedy mode, explore
+ * grabs items that are eligible for autopickup and visits (previously
+ * unvisited) shops.
*
* ***********************************************************************
* called from: acr
* *********************************************************************** */
-void start_explore();
+void start_explore(bool grab_items = false);
struct level_pos;
void start_translevel_travel(const level_pos &pos);
@@ -92,13 +95,15 @@ void arrange_features(std::vector<coord_def> &features);
* *********************************************************************** */
extern short point_distance[GXM][GYM];
-enum EXPLORE_STOP
+enum explore_stop_type
{
- ES_NONE = 0,
- ES_ITEM = 1,
- ES_STAIR = 2,
- ES_SHOP = 4,
- ES_ALTAR = 8
+ ES_NONE = 0x00,
+ ES_ITEM = 0x01,
+ ES_PICKUP = 0x02,
+ ES_GREEDY_PICKUP = 0x04,
+ ES_STAIR = 0x08,
+ ES_SHOP = 0x10,
+ ES_ALTAR = 0x20
};
////////////////////////////////////////////////////////////////////////////
@@ -187,6 +192,40 @@ struct level_pos
void load(FILE *);
};
+// Tracks items discovered by explore in this turn.
+class LevelStashes;
+class explore_discoveries
+{
+public:
+ explore_discoveries();
+
+ void found_feature(const coord_def &pos, int grid);
+ void found_item(const coord_def &pos, const item_def &item);
+
+ // Reports discoveries and prompts the player to stop (if necessary).
+ bool prompt_stop() const;
+
+private:
+ template <class C> void say_any(const C &coll, const char *stub) const;
+ std::string cleaned_feature_description(int feature) const;
+ void add_item(const item_def &item);
+
+private:
+ template <class Z> struct named_thing {
+ std::string name;
+ Z thing;
+
+ named_thing(const std::string &n, Z t) : name(n), thing(t) { }
+ };
+
+ int es_flags;
+ const LevelStashes *current_level;
+ std::vector< named_thing<item_def> > items;
+ std::vector< named_thing<int> > stairs;
+ std::vector< named_thing<int> > shops;
+ std::vector< named_thing<int> > altars;
+};
+
struct stair_info
{
coord_def position; // Position of stair
@@ -345,6 +384,7 @@ private:
int level_distance(level_id first, level_id second);
bool can_travel_interlevel();
+bool prompt_stop_explore(int es_why);
extern TravelCache travel_cache;
diff --git a/crawl-ref/source/view.h b/crawl-ref/source/view.h
index 75f385e1b0..b03af4b81b 100644
--- a/crawl-ref/source/view.h
+++ b/crawl-ref/source/view.h
@@ -114,6 +114,7 @@ void clear_map();
bool is_feature(int feature, int x, int y);
void set_envmap_char( int x, int y, unsigned char chr );
+unsigned get_envmap_char(int x, int y);
void set_envmap_detected_item(int x, int y, bool detected = true);
void set_envmap_detected_mons(int x, int y, bool detected = true);
void set_envmap_col( int x, int y, int colour, int flags );
@@ -125,6 +126,7 @@ void set_terrain_mapped( int x, int y );
void set_terrain_seen( int x, int y );
bool is_terrain_known( int x, int y );
bool is_terrain_seen( int x, int y );
+bool is_terrain_changed( int x, int y );
void clear_feature_overrides();
void add_feature_override(const std::string &text);