diff options
author | dshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573> | 2006-12-30 13:07:34 +0000 |
---|---|---|
committer | dshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573> | 2006-12-30 13:07:34 +0000 |
commit | c9a0cfa661cdad829f2e4af499670b95e3b14336 (patch) | |
tree | 772486cfecdd689ce5b824d4343b7e0c631ee43a | |
parent | 82c469a3a5b523c0b82229d289b122e3868324cc (diff) | |
download | crawl-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.cc | 7 | ||||
-rw-r--r-- | crawl-ref/source/delay.cc | 29 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 9 | ||||
-rw-r--r-- | crawl-ref/source/externs.h | 12 | ||||
-rw-r--r-- | crawl-ref/source/initfile.cc | 112 | ||||
-rw-r--r-- | crawl-ref/source/items.cc | 80 | ||||
-rw-r--r-- | crawl-ref/source/items.h | 4 | ||||
-rw-r--r-- | crawl-ref/source/libutil.h | 27 | ||||
-rw-r--r-- | crawl-ref/source/misc.cc | 13 | ||||
-rw-r--r-- | crawl-ref/source/misc.h | 1 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/stash.cc | 73 | ||||
-rw-r--r-- | crawl-ref/source/stash.h | 14 | ||||
-rw-r--r-- | crawl-ref/source/stuff.cc | 6 | ||||
-rw-r--r-- | crawl-ref/source/stuff.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/travel.cc | 453 | ||||
-rw-r--r-- | crawl-ref/source/travel.h | 54 | ||||
-rw-r--r-- | crawl-ref/source/view.h | 2 |
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); |