summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source
diff options
context:
space:
mode:
authorMatthew Cline <zelgadis@sourceforge.net>2009-10-31 02:16:00 -0700
committerMatthew Cline <zelgadis@sourceforge.net>2009-10-31 02:16:00 -0700
commit59068ce8dae39e2f6a35861d371d0ce6b01732d9 (patch)
treed60a7415f571bc2afcdc5b377bb8bdd349408492 /crawl-ref/source
parentea0a802d98e069bd95b350423546ca727c1b3a2a (diff)
downloadcrawl-ref-59068ce8dae39e2f6a35861d371d0ce6b01732d9.tar.gz
crawl-ref-59068ce8dae39e2f6a35861d371d0ce6b01732d9.zip
Auto-update shopping-list when new items seen
Automatically update the shopping list if you see the same item for less cost in another shop, or if you get an item identical to one on the shopping list (currently only applies to jewellery, books and staves).
Diffstat (limited to 'crawl-ref/source')
-rw-r--r--crawl-ref/source/itemname.cc4
-rw-r--r--crawl-ref/source/itemprop.cc37
-rw-r--r--crawl-ref/source/itemprop.h3
-rw-r--r--crawl-ref/source/items.cc2
-rw-r--r--crawl-ref/source/shopping.cc303
-rw-r--r--crawl-ref/source/shopping.h16
6 files changed, 327 insertions, 38 deletions
diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc
index db5f873da3..a0577bad38 100644
--- a/crawl-ref/source/itemname.cc
+++ b/crawl-ref/source/itemname.cc
@@ -37,6 +37,7 @@
#include "player.h"
#include "religion.h"
#include "quiver.h"
+#include "shopping.h"
#include "skills2.h"
#include "spl-book.h"
#include "state.h"
@@ -1813,6 +1814,9 @@ void set_ident_type( item_def &item, item_type_id_state_type setting,
item_type_id_state_type old_setting = get_ident_type(item);
set_ident_type(item.base_type, item.sub_type, setting, force);
+ if (in_inventory(item))
+ shopping_list.cull_identical_items(item);
+
if (setting == ID_KNOWN_TYPE && old_setting != ID_KNOWN_TYPE
&& notes_are_active() && is_interesting_item(item)
&& !(item.flags & (ISFLAG_NOTED_ID | ISFLAG_NOTED_GET)))
diff --git a/crawl-ref/source/itemprop.cc b/crawl-ref/source/itemprop.cc
index 04854a0fd0..b271b715ce 100644
--- a/crawl-ref/source/itemprop.cc
+++ b/crawl-ref/source/itemprop.cc
@@ -32,6 +32,7 @@
#include "player.h"
#include "quiver.h"
#include "random.h"
+#include "shopping.h"
#include "stuff.h"
#include "transfor.h"
#include "xom.h"
@@ -617,6 +618,9 @@ void set_ident_flags( item_def &item, unsigned long flags )
{
item.flags |= flags;
request_autoinscribe();
+
+ if (in_inventory(item))
+ shopping_list.cull_identical_items(item);
}
if (notes_are_active() && !(item.flags & ISFLAG_NOTED_ID)
@@ -2260,7 +2264,7 @@ bool item_is_staff( const item_def &item )
//
// Ring functions:
-//
+
// Returns number of pluses on jewellery (always none for amulets yet).
int ring_has_pluses( const item_def &item )
{
@@ -2289,6 +2293,37 @@ int ring_has_pluses( const item_def &item )
return (0);
}
+// Returns true if having two rings of the same type on at the same
+// has more effect than just having one on.
+bool ring_has_stackable_effect( const item_def &item )
+{
+ ASSERT (item.base_type == OBJ_JEWELLERY);
+ ASSERT (!jewellery_is_amulet(item));
+
+ if (!item_type_known(item))
+ return (false);
+
+ if (ring_has_pluses(item))
+ return (true);
+
+ switch (item.sub_type)
+ {
+ case RING_PROTECTION_FROM_FIRE:
+ case RING_PROTECTION_FROM_COLD:
+ case RING_LIFE_PROTECTION:
+ case RING_SUSTENANCE:
+ case RING_WIZARDRY:
+ case RING_FIRE:
+ case RING_ICE:
+ return (true);
+
+ default:
+ break;
+ }
+
+ return (false);
+}
+
//
// Food functions:
//
diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h
index 01379741f5..7a08a9c0d8 100644
--- a/crawl-ref/source/itemprop.h
+++ b/crawl-ref/source/itemprop.h
@@ -714,7 +714,8 @@ bool item_is_rod( const item_def &item );
bool item_is_staff( const item_def &item );
// ring functions:
-int ring_has_pluses( const item_def &item );
+int ring_has_pluses( const item_def &item );
+bool ring_has_stackable_effect( const item_def &item );
// food functions:
bool food_is_meat(const item_def &item);
diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc
index d23c559507..2bcb48b407 100644
--- a/crawl-ref/source/items.cc
+++ b/crawl-ref/source/items.cc
@@ -1456,6 +1456,8 @@ int find_free_slot(const item_def &i)
static void _got_item(item_def& item, int quant)
{
+ shopping_list.cull_identical_items(item);
+
if (!is_rune(item))
return;
diff --git a/crawl-ref/source/shopping.cc b/crawl-ref/source/shopping.cc
index 749cb73143..a73af6b9a8 100644
--- a/crawl-ref/source/shopping.cc
+++ b/crawl-ref/source/shopping.cc
@@ -38,6 +38,8 @@
ShoppingList shopping_list;
+static bool _in_shop_now = false;
+
static bool _purchase( int shop, int item_got, int cost, bool id);
static void _shop_print( const char *shoppy, int line )
@@ -54,6 +56,30 @@ static void _shop_more()
get_ch();
}
+static bool _shop_yesno(const char* prompt, int safeanswer)
+{
+ if (_in_shop_now)
+ {
+ textcolor(channel_to_colour(MSGCH_PROMPT));
+ _shop_print(prompt, 1);
+
+ return yesno(NULL, true, safeanswer, false, false, true);
+ }
+ else
+ return yesno(prompt, true, safeanswer, false, false, false);
+}
+
+static void _shop_mpr(const char* msg)
+{
+ if (_in_shop_now)
+ {
+ _shop_print(msg, 1);
+ _shop_more();
+ }
+ else
+ mpr(msg);
+}
+
static std::string _hyphenated_suffix(char prev, char last)
{
std::string s;
@@ -114,7 +140,7 @@ static void _list_shop_keys(const std::string &purchasable, bool viewing,
pkeys = _purchase_keys(purchasable);
std::string shop_list = "";
- if (!viewing)
+ if (!viewing && you.level_type == LEVEL_DUNGEON)
{
shop_list = "[<w>@</w>] ";
if (num_selected > 0)
@@ -274,6 +300,8 @@ static bool _in_a_shop( int shopidx )
{
const shop_struct& shop = env.shop[shopidx];
+ unwind_bool in_shop(_in_shop_now, true);
+
cursor_control coff(false);
clrscr();
@@ -295,9 +323,11 @@ static bool _in_a_shop( int shopidx )
std::vector<bool> selected;
std::vector<bool> in_list;
- const bool id_stock = shoptype_identifies_stock(shop.type);
+ const bool id_stock = shoptype_identifies_stock(shop.type);
bool bought_something = false;
- bool viewing = false;
+ bool viewing = false;
+ bool first_iter = true;
+
while (true)
{
ASSERT(total_cost >= 0);
@@ -346,6 +376,38 @@ static bool _in_a_shop( int shopidx )
_list_shop_keys(purchasable, viewing, stock.size(), num_selected,
num_in_list);
+ // Cull shopping list after shop contents have been displayed, but
+ // only once.
+ if (first_iter)
+ {
+ first_iter = false;
+
+ unsigned int culled = 0;
+
+ for (unsigned int i = 0; i < stock.size(); i++)
+ {
+ const item_def& item = mitm[stock[i]];
+ const int cost = _shop_get_item_value(item, shop.greed,
+ id_stock);
+
+ unsigned int num = shopping_list.cull_identical_items(item,
+ (long) cost);
+ if (num > 0)
+ {
+ in_list[i] = true;
+ num_in_list++;
+ }
+ culled += num;
+ }
+ if (culled > 0)
+ {
+ // Some shopping list items have been moved to this store,
+ // so refresh the display.
+ mesclr();
+ continue;
+ }
+ }
+
if (!total_cost)
{
snprintf(info, INFO_SIZE, "You have %d gold piece%s.", you.gold,
@@ -407,30 +469,60 @@ static bool _in_a_shop( int shopidx )
break;
else if (key == '\r' || key == CK_MOUSE_CLICK)
{
+ std::vector<bool> to_buy;
+ int total_purchase = 0;
+
+ if (num_selected == 0 && num_in_list > 0)
+ {
+ if (_shop_yesno("Buy items on shopping list? (Y/n)", 'y'))
+ {
+ to_buy = in_list;
+
+ for (unsigned int i = 0; i < to_buy.size(); i++)
+ {
+ if (to_buy[i])
+ {
+ const item_def& item = mitm[stock[i]];
+
+ total_purchase +=
+ _shop_get_item_value(item, shop.greed,
+ id_stock);
+ }
+ }
+ }
+ }
+ else
+ {
+ to_buy = selected;
+ total_purchase = total_cost;
+ }
+
// Do purchase.
- if (total_cost > you.gold)
+ if (total_purchase > you.gold)
{
_shop_print("I'm sorry, you don't seem to have enough money.",
1);
}
- else if (!total_cost) // Nothing selected.
+ else if (!total_purchase) // Nothing selected.
continue;
else
{
- textcolor(channel_to_colour(MSGCH_PROMPT));
snprintf(info, INFO_SIZE, "Purchase for %d gold? (y/n) ",
- total_cost);
- _shop_print(info, 1);
+ total_purchase);
- if ( yesno(NULL, true, 'n', false, false, true) )
+ if ( _shop_yesno(info, 'n') )
{
int num_items = 0, outside_items = 0, quant;
- for (int i = selected.size() - 1; i >= 0; --i)
+ for (int i = to_buy.size() - 1; i >= 0; --i)
{
- if (selected[i])
+ if (to_buy[i])
{
item_def& item = mitm[stock[i]];
+ // Remove from shopping list.
+ if (in_list[i])
+ shopping_list.del_thing(item);
+
const int gp_value = _shop_get_item_value(item,
shop.greed, id_stock);
@@ -454,10 +546,6 @@ static bool _in_a_shop( int shopidx )
// knapsack.
outside_items += quant;
}
-
- // Remove from shopping list.
- if (in_list[i])
- shopping_list.del_thing(item);
}
}
@@ -483,7 +571,8 @@ static bool _in_a_shop( int shopidx )
browse_inventory(false);
else if (key == '@')
{
- if (viewing || (num_selected == 0 && num_in_list == 0))
+ if (viewing || (num_selected == 0 && num_in_list == 0)
+ || you.level_type != LEVEL_DUNGEON)
{
_shop_print("Huh?", 1);
_shop_more();
@@ -564,10 +653,8 @@ static bool _in_a_shop( int shopidx )
{
if (gp_value > you.gold)
{
- _shop_print("Remove from shopping list? (y/N)",
- 1);
-
- if ( yesno(NULL, true, 'n', false, false, true) )
+ if ( _shop_yesno("Remove from shopping list? (y/N)",
+ 'n') )
{
in_list[key] = false;
selected[key] = false;
@@ -576,10 +663,8 @@ static bool _in_a_shop( int shopidx )
}
else
{
- _shop_print("Remove item from shopping list and buy "
- "it? (Y/n)", 1);
-
- if ( yesno(NULL, true, 'y', false, false, true) )
+ if ( _shop_yesno("Remove item from shopping list and "
+ "buy it? (Y/n)", 'y') )
{
in_list[key] = false;
// Will be toggled to true later
@@ -2041,6 +2126,13 @@ bool ShoppingList::add_thing(const item_def &item, int cost,
SETUP_POS();
+ if (pos.id.level_type != LEVEL_DUNGEON)
+ {
+ mprf("The shopping list can only contain things in the dungeon.",
+ MSGCH_ERROR);
+ return (false);
+ }
+
if (find_thing(item, pos) != -1)
{
mprf(MSGCH_ERROR, "%s is already on the shopping list.",
@@ -2114,6 +2206,145 @@ bool ShoppingList::del_thing(std::string desc, level_pos* _pos)
#undef SETUP_POS
+#define REMOVE_PROMPTED_KEY "remove_prompted_key"
+#define REPLACE_PROMPTED_KEY "replace_prompted_key"
+
+// TODO:
+//
+// * If you get a randart which lets you turn invisible, then remove
+// any ordinary rings of invisiblity from the shopping list.
+//
+// * If you collected enough spellbooks that all the spells in a
+// shopping list book are covered, then auto-remove it.
+unsigned int ShoppingList::cull_identical_items(const item_def& item,
+ long cost)
+{
+ // Can't put items in Bazaar shops in the shopping list, so
+ // don't bother transfering shopping list items to Bazaar shops.
+ if (cost != -1 && you.level_type != LEVEL_DUNGEON)
+ return (0);
+
+ switch(item.base_type)
+ {
+ case OBJ_JEWELLERY:
+ case OBJ_BOOKS:
+ case OBJ_STAVES:
+ // Only these are really interchangable.
+ break;
+
+ default:
+ return (0);
+ }
+
+ if (!item_type_known(item) || is_artefact(item))
+ return (0);
+
+ // Ignore stat-modification rings which reduce a stat, since they're
+ // worthless.
+ if (item.plus < 0 && item.base_type == OBJ_JEWELLERY)
+ return (0);
+
+ // Item is already on shopping-list.
+ const bool on_list = find_thing(item, level_pos::current()) != -1;
+
+ const bool do_prompt =
+ (item.base_type == OBJ_JEWELLERY && !jewellery_is_amulet(item)
+ && ring_has_stackable_effect(item))
+ // Manuals and tomes of destruction are consumable.
+ || (item.base_type == OBJ_BOOKS
+ && (item.sub_type == BOOK_MANUAL
+ || item.sub_type == BOOK_DESTRUCTION));
+
+ bool add_item = false;
+
+ std::vector<level_pos> to_del;
+
+ // NOTE: Don't modify the shopping list while iterating over it.
+ for (unsigned int i = 0; i < list->size(); i++)
+ {
+ if (_in_shop_now)
+ mesclr();
+
+ CrawlHashTable &thing = (*list)[i];
+
+ if (!thing_is_item(thing))
+ continue;
+
+ const item_def& list_item = get_thing_item(thing);
+
+ if (list_item.base_type != item.base_type
+ || list_item.sub_type != item.sub_type)
+ {
+ continue;
+ }
+
+ if (!item_type_known(list_item) || is_artefact(list_item))
+ continue;
+
+ const level_pos list_pos = thing_pos(thing);
+
+ // cost = -1, we just found a shop item which is cheaper than
+ // one on the shopping list.
+ if (cost != -1)
+ {
+ long list_cost = thing_cost(thing);
+
+ if (cost >= list_cost)
+ continue;
+
+ // Only prompt once.
+ if (thing.exists(REPLACE_PROMPTED_KEY))
+ continue;
+ thing[REPLACE_PROMPTED_KEY] = (bool) true;
+
+ std::string prompt =
+ make_stringf("Shopping-list: replace %dgp %s with cheaper "
+ "one? (Y/n)", list_cost,
+ describe_thing(thing).c_str());
+
+ if (_shop_yesno(prompt.c_str(), 'y'))
+ {
+ add_item = true;
+ to_del.push_back(list_pos);
+ }
+ continue;
+ }
+
+ // cost == -1, we just got an item which is on the shopping list.
+ if (do_prompt)
+ {
+ // Only prompt once.
+ if (thing.exists(REMOVE_PROMPTED_KEY))
+ continue;
+ thing[REMOVE_PROMPTED_KEY] = (bool) true;
+
+ std::string prompt =
+ make_stringf("Shopping-list: remove %s? (Y/n)",
+ describe_thing(thing, DESC_NOCAP_A).c_str());
+
+ if (_shop_yesno(prompt.c_str(), 'y'))
+ to_del.push_back(list_pos);
+ }
+ else
+ {
+ std::string str =
+ make_stringf("Shopping-list: removing %s",
+ describe_thing(thing, DESC_NOCAP_A).c_str());
+
+ _shop_mpr(str.c_str());
+ to_del.push_back(list_pos);
+ }
+ }
+
+ for (unsigned int i = 0; i < to_del.size(); i++)
+ del_thing(item, &to_del[i]);
+
+ if (add_item && !on_list)
+ add_thing(item, cost);
+
+ return (to_del.size());
+}
+
int ShoppingList::size() const
{
if (list == NULL)
@@ -2159,7 +2390,7 @@ void ShoppingList::gold_changed(int old_amount, int new_amount)
if (thing_is_item(thing))
desc = "buy ";
- desc += describe_thing(thing);
+ desc += describe_thing(thing, DESC_NOCAP_A);
descs.push_back(desc);
}
@@ -2234,12 +2465,13 @@ int ShoppingList::find_thing(const item_def &item,
for (unsigned int i = 0; i < list->size(); i++)
{
const CrawlHashTable &thing = (*list)[i];
- const level_pos _pos = thing_pos(thing);
+ const item_def &_item = get_thing_item(thing);
+ const level_pos _pos = thing_pos(thing);
if (pos != _pos)
continue;
- if (item.name(DESC_NOCAP_A) == name_thing(thing))
+ if (item_name_simple(item) == item_name_simple(_item))
return (i);
}
@@ -2286,13 +2518,14 @@ level_pos ShoppingList::thing_pos(const CrawlHashTable& thing)
return (thing[SHOPPING_THING_POS_KEY].get_level_pos());
}
-std::string ShoppingList::name_thing(const CrawlHashTable& thing)
+std::string ShoppingList::name_thing(const CrawlHashTable& thing,
+ description_level_type descrip)
{
if (thing_is_item(thing))
{
const item_def &item = get_thing_item(thing);
- return item.name(DESC_NOCAP_A);
+ return item.name(descrip);
}
else
ASSERT(false);
@@ -2300,11 +2533,12 @@ std::string ShoppingList::name_thing(const CrawlHashTable& thing)
return ("");
}
-std::string ShoppingList::describe_thing(const CrawlHashTable& thing)
+std::string ShoppingList::describe_thing(const CrawlHashTable& thing,
+ description_level_type descrip)
{
const level_pos pos = thing_pos(thing);
- std::string desc = name_thing(thing) + " on ";
+ std::string desc = name_thing(thing, descrip) + " on ";
if (pos.id == level_id::current())
desc += "this level";
@@ -2313,3 +2547,10 @@ std::string ShoppingList::describe_thing(const CrawlHashTable& thing)
return (desc);
}
+
+// Item name without plusses, curse-status or inscription.
+std::string ShoppingList::item_name_simple(const item_def& item, bool ident)
+{
+ return item.name(DESC_PLAIN, false, ident, false, false,
+ ISFLAG_KNOW_CURSE | ISFLAG_KNOW_PLUSES);
+}
diff --git a/crawl-ref/source/shopping.h b/crawl-ref/source/shopping.h
index b74d056899..75ed26a2ac 100644
--- a/crawl-ref/source/shopping.h
+++ b/crawl-ref/source/shopping.h
@@ -46,16 +46,18 @@ public:
bool del_thing(const item_def &item, level_pos* pos = NULL);
bool del_thing(std::string desc, level_pos* pos = NULL);
- int size() const;
-
- void move_things(const coord_def &src, const coord_def &dst);
+ unsigned int cull_identical_items(const item_def& item, long cost = -1);
void gold_changed(int old_amount, int new_amount);
+ void move_things(const coord_def &src, const coord_def &dst);
+
void display_list();
void refresh();
+ int size() const;
+
private:
CrawlVector* list;
@@ -75,8 +77,12 @@ private:
static long thing_cost(const CrawlHashTable& thing);
static level_pos thing_pos(const CrawlHashTable& thing);
- static std::string name_thing(const CrawlHashTable& thing);
- static std::string describe_thing(const CrawlHashTable& thing);
+ static std::string name_thing(const CrawlHashTable& thing,
+ description_level_type descrip = DESC_PLAIN);
+ static std::string describe_thing(const CrawlHashTable& thing,
+ description_level_type descrip = DESC_PLAIN);
+ static std::string item_name_simple(const item_def& item,
+ bool ident = false);
};
extern ShoppingList shopping_list;