The basics of a wish-list/shopping-list. While in a shop the shopping-list can be modified, and the player is notified as soon as they have enough money to buy things on the shopping-list.
OFYW2YRIRSE4TJBFEZ4RROHYVS22T6SJLJXX3FQEBNMWENMBPM4AC
AK73N24BCYLHPTVYVO562AUM6KGLD5DUM572EPX43XYUUKBVOOXQC
T53JH5GFABPQXENOAMN2C2FAMALAMUAMWNEMGKRSO2YEYALLB3TAC
ASXBSZVXJCWVX5VQSRWXL5BWQ4NA7JLYXP6LWQTRGYW6DC2DZZPQC
GWTFRNIDL5TNFFDUENSRVE2EOPSMOAPFBZLESQ3LDDSTSG6EJ33AC
QUXUAWYOV5F333UJDENSORWXQ4DP5XYJATGXSWDOAVSQPKL4FV5AC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
GYRKOLZFYXWJTPEV35USSHCJNA6Y4XMDHSTEZWEBM57WQP2PT6UQC
25EF5X4H3LURXFZ35ZGYGUB6ND7NFQVH4M2XX2SI33I4XRGYG5HAC
Q4TULVRLLNAHZNURPKNAR2K3GBVCEY6KY6ZXKPIJVWVSSZ6R7PEQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
4DEFHDNO2GUBYL4EGYEAR2IP3KBDXRU7UL7L4P7HEZY2UNR6IYJAC
BYSGVGW655QV7SRUP73ZW4UOSFFRA6BV2MYLAIM6QK5CAXSJKGEQC
GP7RUHLNB4KISSZAENEYVNY3MB5YRCFRWRWYAROBZIQ5MR4DXB7QC
DTJQJWFSCI3P6UFRJJXLDIL3DHP3LSFXJKS647VPRWI2DIYZLHFAC
3ZWALZFSTSIVYXY4BAY6ANGINTDACZC6RSSJTEMQSTSUIE66YOBQC
FNXXWDRU5PS3AOL4PLMWBDKJQ2TJOFAWRHTOMEEA6PJOQ7UN566AC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
2LJ3PLU4DNUUCK7SYPTK4G4BTDMJ6UEKOKWML6WOIPJKWLFCD5FQC
YFIVTYI7PMVAXV23DUPXPAQNEY3YSFIXQGSN32I3WVHMMD5XS5DQC
DCZMEKDHQWSQCQYQD6ZXB3XOMWLVLPSPVSBVVMPXMSZF7GO3BVCAC
FAVME2A2U4OUKN2BTISAG5PCI5Y4BN6YVFKCSQQHHZLEVVVF4LKAC
EI7H4ZAWPCJX4SJTRTOULJQHE2UJG746KFLRND7FLZTNG3QIOP4QC
2KTJHQUX2LTU2BCLS5YXVRRKMOYKKIZAPF2LBKORFGSHEN5IO3IAC
NCSALY7HYRVCKTPS5TWZBRE2C3OCANYU3LBPIZ4RRVEI6GOZL2EQC
BMKL2AB2HBBBVAHWDYXPYZHSCO345QWOQHFSICFRN5BCYZ3UEK3AC
DK362IHKSDADMUPD35NOTKM4WESQM37KG2PNOJRV2FGELDWULYPQC
RDOOG5LBE5TCTFYCKJIB7TGGTRFX4HBLMJZYXS5TCFWNCU3QII5QC
LPTP6ZL7U4OVXLZ56TJKJ6HENDHE7ITFMFBXC5BKEBVXEGXPW44AC
PS3OYZOHCD464IMPYTISQO2NDD4WLHZHKAT5TBB2VMAU2JBNPYRQC
IGBJ3ZY5G73GTHPRNWKC6XMQDINRGCTIXKBHTEZ7JLUJS6H4JRBAC
/////////////////////////////////////////////////////////////////////
struct level_pos;
class ShoppingList
{
public:
ShoppingList();
bool add_thing(const item_def &item, int cost, level_pos* pos = NULL);
bool add_thing(std::string desc, std::string buy_verb, int cost,
level_pos* pos = NULL);
bool is_on_list(const item_def &item, level_pos* pos = NULL) const;
bool is_on_list(std::string desc, level_pos* pos = NULL) const;
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);
void gold_changed(int old_amount, int new_amount);
void display_list();
void refresh();
private:
CrawlVector* list;
int min_unbuyable_cost;
int min_unbuyable_idx;
int max_buyable_cost;
int max_buyable_idx;
private:
int find_thing(const item_def &item, const level_pos &pos) const;
int find_thing(const std::string &desc, const level_pos &pos) const;
static bool thing_is_item(const CrawlHashTable& thing);
static const item_def& get_thing_item(const CrawlHashTable& thing);
static std::string get_thing_desc(const CrawlHashTable& thing);
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);
};
extern ShoppingList shopping_list;
const bool id_stock = shoptype_identifies_stock(shop.type);
std::vector<int> stock = _shop_get_stock(shopidx);
// Autoinscribe randarts in the shop.
for (unsigned int i = 0; i < stock.size(); i++)
{
item_def& item = mitm[stock[i]];
if (Options.autoinscribe_artefacts && is_artefact(item))
item.inscription = artefact_auto_inscription(item);
}
// Autoinscribe randarts in the shop.
for (unsigned int i = 0; i < stock.size(); i++)
{
item_def& item = mitm[stock[i]];
if (Options.autoinscribe_artefacts && is_artefact(item))
item.inscription = artefact_auto_inscription(item);
}
stock = _shop_get_stock(shopidx);
else if (key == '@')
{
if (viewing || (num_selected == 0 && num_in_list == 0))
{
_shop_print("Huh?", 1);
_shop_more();
continue;
}
if (num_selected > 0)
{
// Move selected to shopping list.
for (unsigned int i = 0; i < stock.size(); i++)
{
if (selected[i])
{
in_list[i] = true;
selected[i] = false;
}
}
total_cost = 0;
}
else
{
// Move shopping list to selected.
for (unsigned int i = 0; i < stock.size(); i++)
{
if (in_list[i])
{
in_list[i] = false;
selected[i] = true;
const item_def& item = mitm[stock[i]];
total_cost += _shop_get_item_value(item, shop.greed,
id_stock);
}
}
}
}
if (in_list[key])
{
if (gp_value > you.gold)
{
_shop_print("Remove from shopping list? (y/N)",
1);
if ( yesno(NULL, true, 'n', false, false, true) )
{
in_list[key] = false;
selected[key] = false;
}
continue;
}
else
{
_shop_print("Remove item from shopping list and buy "
"it? (Y/n)", 1);
if ( yesno(NULL, true, 'y', false, false, true) )
{
in_list[key] = false;
// Will be toggled to true later
selected[key] = false;
}
else
continue;
}
}
ASSERT(total_cost > 0);
}
}
}
// Actually change shopping list.
for (unsigned int i = 0; i < stock.size(); i++)
{
const item_def& item = mitm[stock[i]];
bool on_list = shopping_list.is_on_list(item);
if (on_list != in_list[i])
{
if (in_list[i])
{
// Ignore Bargaining.
const int cost = _shop_get_item_value(item, shop.greed,
id_stock, false);
shopping_list.add_thing(item, cost);
////////////////////////////////////////////////////////////////////////
// Setup shopping list after restoring savefile.
static void _callback(bool saving)
{
if (!saving)
shopping_list.refresh();
}
static SavefileCallback _register_callback(_callback);
// TODO:
// * Let shopping list be modified from with the stash lister.
// * Warn if buying something not on the shopping list would put
// something on shopping list out of your reach.
#define SHOPPING_LIST_KEY "shopping_list_key"
#define SHOPPING_THING_COST_KEY "cost_key"
#define SHOPPING_THING_ITEM_KEY "item_key"
#define SHOPPING_THING_POS_KEY "pos_key"
ShoppingList::ShoppingList()
{
}
#define SETUP_POS() \
if (list == NULL) \
{ \
mpr("SavefileCallback global constructor weirdness!", MSGCH_ERROR); \
return (false); \
} \
level_pos pos; \
if (_pos != NULL) \
pos = *_pos; \
else \
pos = level_pos::current(); \
ASSERT(pos.is_valid());
#define SETUP_THING() \
CrawlHashTable *thing = new CrawlHashTable(); \
(*thing)[SHOPPING_THING_COST_KEY] = (long) cost; \
(*thing)[SHOPPING_THING_POS_KEY] = pos;
bool ShoppingList::add_thing(const item_def &item, int cost,
level_pos* _pos)
{
ASSERT(is_valid_item(item));
ASSERT(cost > 0);
SETUP_POS();
if (find_thing(item, pos) != -1)
{
mprf(MSGCH_ERROR, "%s is already on the shopping list.",
item.name(DESC_CAP_THE).c_str());
return (false);
}
SETUP_THING();
(*thing)[SHOPPING_THING_ITEM_KEY] = item;
list->push_back(*thing);
refresh();
return (true);
}
bool ShoppingList::add_thing(std::string desc, std::string buy_verb, int cost,
level_pos* _pos)
{
ASSERT(false); // Not implemented yet
SETUP_POS();
return (false);
}
#undef SETUP_THING
bool ShoppingList::is_on_list(const item_def &item, level_pos* _pos) const
{
SETUP_POS();
return (find_thing(item, pos) != -1);
}
bool ShoppingList::is_on_list(std::string desc, level_pos* _pos) const
{
ASSERT(false); // Not implemented yet
SETUP_POS();
return (false);
}
bool ShoppingList::del_thing(const item_def &item, level_pos* _pos)
{
SETUP_POS();
int idx = find_thing(item, pos);
if (idx == -1)
{
mprf(MSGCH_ERROR, "%s isn't on shopping list, can't delete it.",
item.name(DESC_CAP_THE).c_str());
return (false);
}
list->erase(idx);
refresh();
return (true);
}
bool ShoppingList::del_thing(std::string desc, level_pos* _pos)
{
ASSERT(false); // Not implemented yet
SETUP_POS();
return (false);
}
#undef SETUP_POS
int ShoppingList::size() const
{
if (list == NULL)
{
mpr("SavefileCallback global constructor weirdness!", MSGCH_ERROR);
return (0);
}
return ( list->size() );
}
void ShoppingList::move_things(const coord_def &src, const coord_def &dst)
{
ASSERT(false); // Not implemented yet
}
void ShoppingList::gold_changed(int old_amount, int new_amount)
{
if (list == NULL)
{
mpr("SavefileCallback global constructor weirdness!", MSGCH_ERROR);
return;
}
if (new_amount > old_amount && new_amount >= min_unbuyable_cost)
{
ASSERT(min_unbuyable_idx < list->size());
std::vector<std::string> descs;
for (unsigned int i = min_unbuyable_idx; i < list->size(); i++)
{
const CrawlHashTable &thing = (*list)[i];
const long cost = thing_cost(thing);
if (cost > new_amount)
{
ASSERT(i > (unsigned int) min_unbuyable_idx);
break;
}
std::string desc;
if (thing_is_item(thing))
desc = "buy ";
desc += describe_thing(thing);
descs.push_back(desc);
}
ASSERT(!descs.empty());
mpr_comma_separated_list("You now have enough gold to ", descs,
", or ");
// Reset max_buyable and min_unbuyable info
refresh();
}
else if (new_amount < old_amount && new_amount < max_buyable_cost)
{
// Reset max_buyable and min_unbuyable info
refresh();
}
}
void ShoppingList::display_list()
{
ASSERT(false); // Not implemented yet
}
bool _compare_shopping_things(const CrawlStoreValue& a,
const CrawlStoreValue& b)
{
const CrawlHashTable& hash_a = a.get_table();
const CrawlHashTable& hash_b = b.get_table();
const long a_cost = hash_a[SHOPPING_THING_COST_KEY];
const long b_cost = hash_b[SHOPPING_THING_COST_KEY];
return (a_cost < b_cost);
}
void ShoppingList::refresh()
{
if (!you.props.exists(SHOPPING_LIST_KEY))
you.props[SHOPPING_LIST_KEY].new_vector(SV_HASH, SFLAG_CONST_TYPE);
list = &you.props[SHOPPING_LIST_KEY].get_vector();
std::sort(list->begin(), list->end(), _compare_shopping_things);
min_unbuyable_cost = INT_MAX;
min_unbuyable_idx = -1;
max_buyable_cost = -1;
max_buyable_idx = -1;
for (unsigned int i = 0; i < list->size(); i++)
{
const CrawlHashTable &thing = (*list)[i];
const long cost = thing_cost(thing);
if (cost <= you.gold)
{
max_buyable_cost = cost;
max_buyable_idx = i;
}
else
{
min_unbuyable_cost = cost;
min_unbuyable_idx = i;
break;
}
}
}
int ShoppingList::find_thing(const item_def &item,
const level_pos &pos) const
{
for (unsigned int i = 0; i < list->size(); i++)
{
const CrawlHashTable &thing = (*list)[i];
const level_pos _pos = thing_pos(thing);
if (pos != _pos)
continue;
if (item.name(DESC_NOCAP_A) == name_thing(thing))
return (i);
}
return (-1);
}
int ShoppingList::find_thing(const std::string &desc,
const level_pos &pos) const
{
ASSERT(false); // Not implemented yet
return (-1);
}
bool ShoppingList::thing_is_item(const CrawlHashTable& thing)
{
return thing.exists(SHOPPING_THING_ITEM_KEY);
}
const item_def& ShoppingList::get_thing_item(const CrawlHashTable& thing)
{
ASSERT(thing.exists(SHOPPING_THING_ITEM_KEY));
const item_def &item = thing[SHOPPING_THING_ITEM_KEY].get_item();
ASSERT(is_valid_item(item));
return (item);
}
std::string ShoppingList::get_thing_desc(const CrawlHashTable& thing)
{
ASSERT(false); // Not implemented yet
return("");
}
long ShoppingList::thing_cost(const CrawlHashTable& thing)
{
ASSERT(thing.exists(SHOPPING_THING_COST_KEY));
return (thing[SHOPPING_THING_COST_KEY].get_long());
}
level_pos ShoppingList::thing_pos(const CrawlHashTable& thing)
{
ASSERT(thing.exists(SHOPPING_THING_POS_KEY));
return (thing[SHOPPING_THING_POS_KEY].get_level_pos());
}
std::string ShoppingList::name_thing(const CrawlHashTable& thing)
{
if (thing_is_item(thing))
{
const item_def &item = get_thing_item(thing);
return item.name(DESC_NOCAP_A);
}
else
ASSERT(false);
return ("");
}
std::string ShoppingList::describe_thing(const CrawlHashTable& thing)
{
const level_pos pos = thing_pos(thing);
std::string desc = name_thing(thing) + " on ";
if (pos.id == level_id::current())
desc += "this level";
else
desc += pos.id.describe();
return (desc);
}
void player::set_gold(int amount)
{
ASSERT(amount >= 0);
if (amount != gold)
{
shopping_list.gold_changed(gold, amount);
gold = amount;
}
}