3 * Summary: Shop keeper functions.
4 * Written by: Linley Henzell
22 #include "dgn-overview.h"
41 #define SHOPPING_LIST_COST_KEY "shopping_list_cost_key"
43 ShoppingList shopping_list
;
45 static bool _in_shop_now
= false;
47 static bool _purchase(int shop
, int item_got
, int cost
, bool id
);
49 static void _shop_print(const char *shoppy
, int line
)
51 cgotoxy(1, line
+ 19, GOTO_CRT
);
52 cprintf("%s", shoppy
);
53 clear_to_end_of_line();
56 static void _shop_more()
58 cgotoxy(65, 20, GOTO_CRT
);
63 static bool _shop_yesno(const char* prompt
, int safeanswer
)
67 textcolor(channel_to_colour(MSGCH_PROMPT
));
68 _shop_print(prompt
, 1);
70 return yesno(NULL
, true, safeanswer
, false, false, true);
73 return yesno(prompt
, true, safeanswer
, false, false, false);
76 static void _shop_mpr(const char* msg
)
87 static std::string
_hyphenated_suffix(char prev
, char last
)
92 else if (prev
== last
+ 2)
93 s
+= (char) (last
+ 1);
100 static std::string
_purchase_keys(const std::string
&s
)
105 std::string list
= "<w>" + s
.substr(0, 1);
107 for (unsigned int i
= 1; i
< s
.length(); ++i
)
109 if (s
[i
] == s
[i
- 1] + 1)
112 char prev
= s
[i
- 1];
113 list
+= _hyphenated_suffix(prev
, last
);
114 list
+= (last
= s
[i
]);
117 list
+= _hyphenated_suffix(s
[s
.length() - 1], last
);
122 static void _list_shop_keys(const std::string
&purchasable
, bool viewing
,
123 int total_stock
, int num_selected
,
126 ASSERT(total_stock
> 0);
128 const int numlines
= get_number_of_lines();
131 std::string shop_list
= "";
132 if (!viewing
&& you
.level_type
== LEVEL_DUNGEON
)
134 shop_list
= "[<w>$</w>] ";
135 if (num_selected
> 0)
136 shop_list
+= "Selected -> shopping list";
137 else if (num_in_list
> 0)
138 shop_list
+= "Shopping list -> selected";
142 if (!shop_list
.empty())
144 cgotoxy(1, numlines
- 2, GOTO_CRT
);
145 fs
= formatted_string::parse_string(shop_list
);
146 fs
.cprintf("%*s", get_number_of_cols() - fs
.length() - 1, "");
150 cgotoxy(1, numlines
- 1, GOTO_CRT
);
152 std::string pkeys
= "";
159 pkeys
+= 'a' + total_stock
- 1;
164 pkeys
= _purchase_keys(purchasable
);
168 pkeys
= "[" + pkeys
+ "] Select Item to "
169 + (viewing
? "Examine" : "Buy");
171 fs
= formatted_string::parse_string(make_stringf(
172 "[<w>x</w>/<w>Esc</w>"
176 "] exit [<w>!</w>] %s %s",
177 (viewing
? "to buy items " : "to examine items"),
180 fs
.cprintf("%*s", get_number_of_cols() - fs
.length() - 1, "");
182 cgotoxy(1, numlines
, GOTO_CRT
);
184 fs
= formatted_string::parse_string(
189 "] make purchase [<w>\\</w>] list known items "
190 "[<w>?</w>/<w>*</w>] inventory");
192 fs
.cprintf("%*s", get_number_of_cols() - fs
.length() - 1, "");
196 static std::vector
<int> _shop_get_stock(int shopidx
)
198 std::vector
<int> result
;
199 // Shop items are heaped up at this cell.
200 const coord_def
stack_location(0, 5 + shopidx
);
201 for (stack_iterator
si(stack_location
); si
; ++si
)
202 result
.push_back(si
.link());
206 static int _shop_get_item_value(const item_def
& item
, int greed
, bool id
,
207 bool ignore_bargain
= false)
209 int result
= (greed
* item_value(item
, id
) / 10);
210 if (you
.duration
[DUR_BARGAIN
] && !ignore_bargain
) // 20% discount
222 static std::string
_shop_print_stock(const std::vector
<int>& stock
,
223 const std::vector
<bool>& selected
,
224 const std::vector
<bool>& in_list
,
225 const shop_struct
& shop
,
228 ShopInfo
&si
= StashTrack
.get_shop(shop
.pos
);
229 const bool id
= shoptype_identifies_stock(shop
.type
);
230 std::string purchasable
;
231 for (unsigned int i
= 0; i
< stock
.size(); ++i
)
233 const item_def
& item
= mitm
[stock
[i
]];
234 const int gp_value
= _shop_get_item_value(item
, shop
.greed
, id
);
235 const bool can_afford
= (you
.gold
>= gp_value
);
237 cgotoxy(1, i
+1, GOTO_CRT
);
238 const char c
= i
+ 'a';
242 // Colour stock as follows:
243 // * lightcyan, if on the shopping list.
244 // * lightred, if you can't buy all you selected.
245 // * lightgreen, if this item is purchasable along with your selections
246 // * red, if this item is not purchasable even by itself.
247 // * yellow, if this item would be purchasable if you deselected
250 // Is this too complicated? (jpeg)
253 textcolor(LIGHTCYAN
);
254 else if (total_cost
> you
.gold
&& selected
[i
])
256 else if (gp_value
<= you
.gold
- total_cost
|| selected
[i
] && can_afford
)
257 textcolor(LIGHTGREEN
);
258 else if (!can_afford
)
265 else if (selected
[i
])
270 if (Options
.menu_colour_shops
)
272 // Colour stock according to menu colours.
273 const std::string colprf
= menu_colour_item_prefix(item
);
274 const int col
= menu_colour(item
.name(DESC_NOCAP_A
),
276 textcolor(col
!= -1 ? col
: LIGHTGREY
);
279 textcolor(i
% 2 ? LIGHTGREY
: WHITE
);
281 cprintf("%-56s%5d gold",
282 item
.name(DESC_NOCAP_A
, false, id
).substr(0, 56).c_str(),
285 si
.add_item(item
, gp_value
);
287 textcolor(LIGHTGREY
);
289 return (purchasable
);
292 static int _count_identical(const std::vector
<int>& stock
,
293 const item_def
& item
)
296 for (unsigned int i
= 0; i
< stock
.size(); i
++)
298 const item_def
&other
= mitm
[stock
[i
]];
300 if (ShoppingList::items_are_same(item
, other
))
306 // Rather than prompting for each individual item, shopping now works more
307 // like multi-pickup, in that pressing a letter only "selects" an item
308 // (changing the '-' next to its name to a '+'). Affordability is shown
309 // via colours that are updated every time the contents of your shopping
312 // New, suggested shopping keys:
313 // * letter keys [a-t] (de)select item, as now
314 // * Enter buys (with prompt), as now
315 // * \ shows discovered items, as now
316 // * x exits (also Esc), as now
317 // * ! toggles examination mode (where letter keys view items)
318 // * *, ? lists inventory
320 // For the ? key, the text should read:
321 // [!] switch to examination mode
322 // [!] switch to selection mode
323 static bool _in_a_shop(int shopidx
, int &num_in_list
)
325 const shop_struct
& shop
= env
.shop
[shopidx
];
327 unwind_bool
in_shop(_in_shop_now
, true);
329 cursor_control
coff(false);
333 const std::string hello
= "Welcome to " + shop_name(shop
.pos
) + "!";
337 std::vector
<int> stock
= _shop_get_stock(shopidx
);
339 // Autoinscribe randarts in the shop.
340 for (unsigned int i
= 0; i
< stock
.size(); i
++)
342 item_def
& item
= mitm
[stock
[i
]];
343 if (Options
.autoinscribe_artefacts
&& is_artefact(item
))
344 item
.inscription
= artefact_auto_inscription(item
);
347 std::vector
<bool> selected
;
348 std::vector
<bool> in_list
;
350 const bool id_stock
= shoptype_identifies_stock(shop
.type
);
351 bool bought_something
= false;
352 bool viewing
= false;
353 bool first_iter
= true;
357 ASSERT(total_cost
>= 0);
359 StashTrack
.get_shop(shop
.pos
).reset();
361 stock
= _shop_get_stock(shopidx
);
364 in_list
.resize(stock
.size(), false);
365 for (unsigned int i
= 0; i
< stock
.size(); i
++)
367 const item_def
& item
= mitm
[stock
[i
]];
368 in_list
[i
] = shopping_list
.is_on_list(item
);
371 // If items have been bought...
372 if (stock
.size() != selected
.size())
376 selected
.resize(stock
.size(), false);
380 int num_selected
= 0;
381 for (unsigned int i
= 0; i
< stock
.size(); i
++)
392 _shop_print("I'm sorry, my shop is empty now.", 1);
394 return (bought_something
);
397 const std::string purchasable
= _shop_print_stock(stock
, selected
,
400 _list_shop_keys(purchasable
, viewing
, stock
.size(), num_selected
,
403 // Cull shopping list after shop contents have been displayed, but
409 unsigned int culled
= 0;
411 for (unsigned int i
= 0; i
< stock
.size(); i
++)
413 const item_def
& item
= mitm
[stock
[i
]];
414 const int cost
= _shop_get_item_value(item
, shop
.greed
,
417 unsigned int num
= shopping_list
.cull_identical_items(item
,
428 // Some shopping list items have been moved to this store,
429 // so refresh the display.
436 snprintf(info
, INFO_SIZE
, "You have %d gold piece%s.", you
.gold
,
437 you
.gold
!= 1 ? "s" : "");
441 else if (total_cost
> you
.gold
)
443 snprintf(info
, INFO_SIZE
, "You have %d gold piece%s. "
444 "You are short %d gold piece%s for the purchase.",
446 you
.gold
!= 1 ? "s" : "",
447 total_cost
- you
.gold
,
448 (total_cost
- you
.gold
!= 1) ? "s" : "");
454 snprintf(info
, INFO_SIZE
, "You have %d gold piece%s. "
455 "After the purchase, you will have %d gold piece%s.",
457 you
.gold
!= 1 ? "s" : "",
458 you
.gold
- total_cost
,
459 (you
.gold
- total_cost
!= 1) ? "s" : "");
464 _shop_print(info
, 0);
469 snprintf(info
, INFO_SIZE
, "%s What would you like to do? ",
473 snprintf(info
, INFO_SIZE
, "What would you like to do? ");
476 _shop_print(info
, 1);
478 textcolor(LIGHTGREY
);
480 mouse_control
mc(MOUSE_MODE_MORE
);
484 check_item_knowledge();
485 else if (key
== 'x' || key_is_escape(key
) || key
== CK_MOUSE_CMD
)
487 else if (key
== '\r' || key
== CK_MOUSE_CLICK
)
489 std::vector
<bool> to_buy
;
490 int total_purchase
= 0;
492 if (num_selected
== 0 && num_in_list
> 0)
494 if (_shop_yesno("Buy items on shopping list? (Y/n)", 'y'))
498 for (unsigned int i
= 0; i
< to_buy
.size(); i
++)
502 const item_def
& item
= mitm
[stock
[i
]];
505 _shop_get_item_value(item
, shop
.greed
,
514 total_purchase
= total_cost
;
518 if (total_purchase
> you
.gold
)
520 _shop_print("I'm sorry, you don't seem to have enough money.",
523 else if (!total_purchase
) // Nothing selected.
527 snprintf(info
, INFO_SIZE
, "Purchase for %d gold? (y/n)",
530 if (_shop_yesno(info
, 'n'))
532 int num_items
= 0, outside_items
= 0, quant
;
533 for (int i
= to_buy
.size() - 1; i
>= 0; --i
)
537 item_def
& item
= mitm
[stock
[i
]];
539 // Remove from shopping list if it's unique
540 // (i.e., if the shop has multiple scrolls of
541 // identify, don't remove the other scrolls
542 // from the shopping list if there's any
545 && _count_identical(stock
, item
) == 1)
547 shopping_list
.del_thing(item
);
550 const int gp_value
= _shop_get_item_value(item
,
551 shop
.greed
, id_stock
);
553 // Take a note of the purchase.
554 take_note(Note(NOTE_BUY_ITEM
, gp_value
, 0,
555 item
.name(DESC_NOCAP_A
).c_str()));
557 // But take no further similar notes.
558 item
.flags
|= ISFLAG_NOTED_GET
;
560 if (fully_identified(item
))
561 item
.flags
|= ISFLAG_NOTED_ID
;
563 quant
= item
.quantity
;
566 if (!_purchase(shopidx
, stock
[i
], gp_value
,
569 // The purchased item didn't fit into your
571 outside_items
+= quant
;
578 mprf("I'll put %s outside for you.",
579 num_items
== 1 ? "it" :
580 num_items
== outside_items
? "them"
583 bought_something
= true;
591 // Toggle between browsing and shopping.
594 else if (key
== '?' || key
== '*')
595 browse_inventory(false);
598 if (viewing
|| (num_selected
== 0 && num_in_list
== 0)
599 || you
.level_type
!= LEVEL_DUNGEON
)
601 _shop_print("Huh?", 1);
606 if (num_selected
> 0)
608 // Move selected to shopping list.
609 for (unsigned int i
= 0; i
< stock
.size(); i
++)
611 const item_def
&item
= mitm
[stock
[i
]];
614 if (!shopping_list
.is_on_list(item
))
616 // Ignore Bargaining.
617 const int cost
= _shop_get_item_value(item
,
618 shop
.greed
, id_stock
, false);
619 shopping_list
.add_thing(item
, cost
);
629 // Move shopping list to selected.
630 for (unsigned int i
= 0; i
< stock
.size(); i
++)
632 const item_def
&item
= mitm
[stock
[i
]];
638 total_cost
+= _shop_get_item_value(item
, shop
.greed
,
641 if (shopping_list
.is_on_list(item
))
642 shopping_list
.del_thing(item
);
647 else if (!isaalpha(key
))
649 _shop_print("Huh?", 1);
654 key
= tolower(key
) - 'a';
655 if (key
>= static_cast<int>(stock
.size()))
657 _shop_print("No such item.", 1);
662 item_def
& item
= mitm
[stock
[key
]];
665 // A hack to make the description more useful.
666 // In theory, the user could kill the process at this
667 // point and end up with valid ID for the item.
668 // That's not very useful, though, because it doesn't set
669 // type-ID and once you can access the item (by buying it)
670 // you have its full ID anyway. Worst case, it won't get
671 // noted when you buy it.
672 const uint64_t old_flags
= item
.flags
;
675 item
.flags
|= (ISFLAG_IDENT_MASK
| ISFLAG_NOTED_ID
678 describe_item(item
, false, true);
680 item
.flags
= old_flags
;
684 const int gp_value
= _shop_get_item_value(item
, shop
.greed
,
689 if (gp_value
> you
.gold
)
691 if (_shop_yesno("Remove from shopping list? (y/N)",
694 shopping_list
.del_thing(item
);
695 in_list
[key
] = false;
696 selected
[key
] = false;
702 if (_shop_yesno("Remove item from shopping list and "
703 "buy it? (Y/n)", 'y'))
705 shopping_list
.del_thing(item
);
706 in_list
[key
] = false;
707 // Will be toggled to true later
708 selected
[key
] = false;
715 selected
[key
] = !selected
[key
];
717 total_cost
+= gp_value
;
719 total_cost
-= gp_value
;
721 ASSERT(total_cost
>= 0);
726 return (bought_something
);
729 bool shoptype_identifies_stock(shop_type type
)
731 return (type
!= SHOP_WEAPON_ANTIQUE
732 && type
!= SHOP_ARMOUR_ANTIQUE
733 && type
!= SHOP_GENERAL_ANTIQUE
);
736 static bool _purchase(int shop
, int item_got
, int cost
, bool id
)
740 you
.attribute
[ATTR_PURCHASES
] += cost
;
742 item_def
& item
= mitm
[item_got
];
744 origin_purchased(item
);
748 // Identify the item and its type.
749 // This also takes the ID note if necessary.
750 set_ident_type(item
, ID_KNOWN_TYPE
);
751 set_ident_flags(item
, ISFLAG_IDENT_MASK
);
754 const int quant
= item
.quantity
;
755 // Note that item will be invalidated if num == item.quantity.
756 const int num
= move_item_to_player(item_got
, item
.quantity
, true);
758 // Shopkeepers will now place goods you can't carry outside the shop.
761 move_item_to_grid(&item_got
, env
.shop
[shop
].pos
);
767 // This probably still needs some work. Rings used to be the only
768 // artefacts which had a change in price, and that value corresponds
769 // to returning 50 from this function. Good artefacts will probably
770 // be returning just over 30 right now. Note that this isn't used
771 // as a multiple, its used in the old ring way: 7 * ret is added to
772 // the price of the artefact. -- bwr
773 int artefact_value(const item_def
&item
)
775 ASSERT(is_artefact(item
));
778 artefact_properties_t prop
;
779 artefact_wpn_properties(item
, prop
);
781 // Brands are already accounted for via existing ego checks
783 // This should probably be more complex... but this isn't so bad:
784 ret
+= 3 * prop
[ ARTP_AC
] + 3 * prop
[ ARTP_EVASION
]
785 + 3 * prop
[ ARTP_ACCURACY
] + 3 * prop
[ ARTP_DAMAGE
]
786 + 6 * prop
[ ARTP_STRENGTH
] + 6 * prop
[ ARTP_INTELLIGENCE
]
787 + 6 * prop
[ ARTP_DEXTERITY
];
789 // These resistances have meaningful levels
790 if (prop
[ ARTP_FIRE
] > 0)
791 ret
+= 5 + 5 * (prop
[ ARTP_FIRE
] * prop
[ ARTP_FIRE
]);
792 else if (prop
[ ARTP_FIRE
] < 0)
795 if (prop
[ ARTP_COLD
] > 0)
796 ret
+= 5 + 5 * (prop
[ ARTP_COLD
] * prop
[ ARTP_COLD
]);
797 else if (prop
[ ARTP_COLD
] < 0)
800 if (prop
[ARTP_PONDEROUS
])
803 // These normally come alone or in resist/susceptible pairs...
804 // we're making items a bit more expensive if they have both positive.
805 if (prop
[ ARTP_FIRE
] > 0 && prop
[ ARTP_COLD
] > 0)
808 if (prop
[ ARTP_NEGATIVE_ENERGY
] > 0)
809 ret
+= 5 + 5 * (prop
[ARTP_NEGATIVE_ENERGY
] * prop
[ARTP_NEGATIVE_ENERGY
]);
811 // only one meaningful level:
812 if (prop
[ ARTP_POISON
])
815 // only one meaningful level (hard to get):
816 if (prop
[ ARTP_ELECTRICITY
])
819 // magic resistance is from 35-100
820 if (prop
[ ARTP_MAGIC
])
821 ret
+= 5 + prop
[ ARTP_MAGIC
] / 15;
823 if (prop
[ ARTP_EYESIGHT
])
827 if (prop
[ ARTP_LEVITATE
])
830 if (prop
[ ARTP_BLINK
])
833 if (prop
[ ARTP_BERSERK
])
836 if (prop
[ ARTP_INVISIBLE
])
839 if (prop
[ ARTP_ANGRY
])
842 if (prop
[ ARTP_CAUSE_TELEPORTATION
])
845 if (prop
[ ARTP_NOISES
])
848 if (prop
[ ARTP_PREVENT_TELEPORTATION
])
851 if (prop
[ ARTP_PREVENT_SPELLCASTING
])
855 if (prop
[ ARTP_MUTAGENIC
])
856 ret
-= (5 + 3 * prop
[ ARTP_MUTAGENIC
]);
859 if (prop
[ ARTP_METABOLISM
])
860 ret
-= (2 * prop
[ ARTP_METABOLISM
]);
862 return ((ret
> 0) ? ret
: 0);
865 unsigned int item_value(item_def item
, bool ident
)
867 // Note that we pass item in by value, since we want a local
868 // copy to mangle as necessary.
869 item
.flags
= (ident
) ? (item
.flags
| ISFLAG_IDENT_MASK
) : (item
.flags
);
871 if (is_unrandom_artefact(item
)
872 && item_ident(item
, ISFLAG_KNOW_PROPERTIES
))
874 const unrandart_entry
*entry
= get_unrand_entry(item
.special
);
875 if (entry
->value
!= 0)
876 return (entry
->value
);
881 switch (item
.base_type
)
884 switch (item
.sub_type
)
899 case WPN_GIANT_SPIKED_CLUB
:
927 case WPN_QUARTERSTAFF
:
928 case WPN_SHORT_SWORD
:
939 case WPN_MORNINGSTAR
:
955 case WPN_BLESSED_FALCHION
:
959 case WPN_SPIKED_FLAIL
:
960 case WPN_BLESSED_LONG_SWORD
:
961 case WPN_BLESSED_SCIMITAR
:
973 case WPN_GREAT_SWORD
:
979 case WPN_EVENINGSTAR
:
988 case WPN_EXECUTIONERS_AXE
:
992 case WPN_DOUBLE_SWORD
:
1000 case WPN_QUICK_BLADE
:
1001 case WPN_DEMON_TRIDENT
:
1006 case WPN_DEMON_BLADE
:
1007 case WPN_TRIPLE_SWORD
:
1008 case WPN_BLESSED_KATANA
:
1009 case WPN_EUDEMON_BLADE
:
1010 case WPN_BLESSED_DOUBLE_SWORD
:
1011 case WPN_BLESSED_GREAT_SWORD
:
1012 case WPN_BLESSED_TRIPLE_SWORD
:
1013 case WPN_SACRED_SCOURGE
:
1020 if (item_type_known(item
))
1022 switch (get_weapon_brand(item
))
1029 case SPWPN_DRAINING
:
1033 case SPWPN_VAMPIRICISM
:
1039 case SPWPN_HOLY_WRATH
:
1040 case SPWPN_REACHING
:
1041 case SPWPN_RETURNING
:
1050 case SPWPN_DISTORTION
:
1051 case SPWPN_ELECTROCUTION
:
1057 case SPWPN_FREEZING
:
1058 case SPWPN_DRAGON_SLAYING
:
1066 case SPWPN_ORC_SLAYING
:
1071 case SPWPN_PROTECTION
:
1080 if (get_equip_race(item
) == ISFLAG_ELVEN
1081 || get_equip_race(item
) == ISFLAG_DWARVEN
)
1087 if (get_equip_race(item
) == ISFLAG_ORCISH
)
1093 if (item_ident(item
, ISFLAG_KNOW_PLUSES
))
1097 valued
+= item
.plus
* 2;
1098 valued
*= 10 + 3 * item
.plus
;
1102 if (item
.plus2
>= 0)
1104 valued
+= item
.plus2
* 2;
1105 valued
*= 10 + 3 * item
.plus2
;
1112 valued
+= (item
.plus
* item
.plus
* item
.plus
);
1121 valued
+= (item
.plus2
* item
.plus2
* item
.plus2
);
1128 if (is_artefact(item
))
1130 if (item_type_known(item
))
1131 valued
+= (7 * artefact_value(item
));
1135 else if (item_type_known(item
)
1136 && get_equip_desc(item
) != 0)
1141 if (item_known_cursed(item
))
1148 case OBJ_MISSILES
: // ammunition
1149 switch (item
.sub_type
)
1167 case MI_THROWING_NET
:
1175 if (item_type_known(item
))
1177 switch (get_ammo_brand(item
))
1184 case SPMSL_RETURNING
:
1193 case SPMSL_PENETRATION
:
1197 case SPMSL_DISPERSAL
:
1198 case SPMSL_EXPLODING
:
1207 case SPMSL_POISONED
:
1208 case SPMSL_PARALYSIS
:
1211 case SPMSL_CONFUSION
:
1212 case SPMSL_SICKNESS
:
1221 if (get_equip_race(item
) == ISFLAG_ELVEN
1222 || get_equip_race(item
) == ISFLAG_DWARVEN
)
1228 if (get_equip_race(item
) == ISFLAG_ORCISH
)
1234 if (item_ident(item
, ISFLAG_KNOW_PLUSES
))
1237 valued
+= (item
.plus
* 2);
1241 valued
+= item
.plus
* item
.plus
* item
.plus
;
1250 switch (item
.sub_type
)
1252 case ARM_PEARL_DRAGON_ARMOUR
:
1253 case ARM_GOLD_DRAGON_ARMOUR
:
1257 case ARM_PEARL_DRAGON_HIDE
:
1258 case ARM_GOLD_DRAGON_HIDE
:
1262 case ARM_STORM_DRAGON_ARMOUR
:
1266 case ARM_STORM_DRAGON_HIDE
:
1270 case ARM_DRAGON_ARMOUR
:
1271 case ARM_ICE_DRAGON_ARMOUR
:
1275 case ARM_SWAMP_DRAGON_ARMOUR
:
1279 case ARM_DRAGON_HIDE
:
1280 case ARM_CRYSTAL_PLATE_MAIL
:
1281 case ARM_TROLL_LEATHER_ARMOUR
:
1282 case ARM_ICE_DRAGON_HIDE
:
1286 case ARM_MOTTLED_DRAGON_ARMOUR
:
1287 case ARM_SWAMP_DRAGON_HIDE
:
1291 case ARM_STEAM_DRAGON_ARMOUR
:
1292 case ARM_MOTTLED_DRAGON_HIDE
:
1296 case ARM_PLATE_MAIL
:
1300 case ARM_STEAM_DRAGON_HIDE
:
1304 case ARM_BANDED_MAIL
:
1305 case ARM_CENTAUR_BARDING
:
1306 case ARM_NAGA_BARDING
:
1310 case ARM_SPLINT_MAIL
:
1314 case ARM_TROLL_HIDE
:
1318 case ARM_CHAIN_MAIL
:
1322 case ARM_SCALE_MAIL
:
1326 case ARM_LARGE_SHIELD
:
1340 case ARM_WIZARD_HAT
:
1345 case ARM_LEATHER_ARMOUR
:
1365 case ARM_ANIMAL_SKIN
:
1370 if (item_type_known(item
))
1372 const int sparm
= get_armour_ego_type(item
);
1380 case SPARM_ARCHMAGI
:
1384 case SPARM_DARKNESS
:
1385 case SPARM_RESISTANCE
:
1386 case SPARM_REFLECTION
:
1390 case SPARM_POSITIVE_ENERGY
:
1394 case SPARM_MAGIC_RESISTANCE
:
1395 case SPARM_PROTECTION
:
1400 case SPARM_COLD_RESISTANCE
:
1401 case SPARM_DEXTERITY
:
1402 case SPARM_FIRE_RESISTANCE
:
1403 case SPARM_SEE_INVISIBLE
:
1404 case SPARM_INTELLIGENCE
:
1405 case SPARM_LEVITATION
:
1406 case SPARM_PRESERVATION
:
1408 case SPARM_STRENGTH
:
1412 case SPARM_POISON_RESISTANCE
:
1416 case SPARM_PONDEROUSNESS
:
1424 if (get_equip_race(item
) == ISFLAG_ELVEN
1425 || get_equip_race(item
) == ISFLAG_DWARVEN
)
1431 if (get_equip_race(item
) == ISFLAG_ORCISH
)
1437 if (item_ident(item
, ISFLAG_KNOW_PLUSES
))
1442 valued
+= item
.plus
* 30;
1443 valued
*= 10 + 4 * item
.plus
;
1449 valued
+= item
.plus
* item
.plus
* item
.plus
;
1456 if (is_artefact(item
))
1458 if (item_type_known(item
))
1459 valued
+= (7 * artefact_value(item
));
1463 else if (item_type_known(item
) && get_equip_desc(item
) != 0)
1468 if (item_known_cursed(item
))
1476 if (!item_type_known(item
))
1480 switch (item
.sub_type
)
1487 case WAND_TELEPORTATION
:
1497 case WAND_INVISIBILITY
:
1499 case WAND_LIGHTNING
:
1503 case WAND_DISINTEGRATION
:
1508 case WAND_PARALYSIS
:
1517 case WAND_ENSLAVEMENT
:
1518 case WAND_POLYMORPH_OTHER
:
1522 case WAND_CONFUSION
:
1527 case WAND_MAGIC_DARTS
:
1528 case WAND_RANDOM_EFFECTS
:
1534 if (item_ident(item
, ISFLAG_KNOW_PLUSES
))
1539 valued
= (valued
* (item
.plus
+ 45)) / 50;
1545 if (!item_type_known(item
))
1549 switch (item
.sub_type
)
1551 case POT_EXPERIENCE
:
1555 case POT_GAIN_DEXTERITY
:
1556 case POT_GAIN_INTELLIGENCE
:
1557 case POT_GAIN_STRENGTH
:
1561 case POT_CURE_MUTATION
:
1566 case POT_RESISTANCE
:
1570 case POT_INVISIBILITY
:
1575 case POT_RESTORE_ABILITIES
:
1579 case POT_BERSERK_RAGE
:
1580 case POT_HEAL_WOUNDS
:
1586 case POT_BRILLIANCE
:
1592 case POT_LEVITATION
:
1601 case POT_BLOOD_COAGULATED
:
1607 case POT_DEGENERATION
:
1611 case POT_STRONG_POISON
:
1620 switch (item
.sub_type
)
1622 case FOOD_ROYAL_JELLY
:
1626 case FOOD_MEAT_RATION
:
1627 case FOOD_BREAD_RATION
:
1631 case FOOD_HONEYCOMB
:
1635 case FOOD_BEEF_JERKY
:
1658 if (food_is_rotten(item
))
1664 case FOOD_SNOZZCUMBER
:
1668 case FOOD_STRAWBERRY
:
1677 if (!item_type_known(item
))
1681 switch (item
.sub_type
)
1683 case SCR_ACQUIREMENT
:
1687 case SCR_ENCHANT_WEAPON_III
:
1688 case SCR_VORPALISE_WEAPON
:
1699 case SCR_VULNERABILITY
:
1703 case SCR_ENCHANT_WEAPON_II
:
1707 case SCR_RECHARGING
:
1712 case SCR_ENCHANT_ARMOUR
:
1713 case SCR_ENCHANT_WEAPON_I
:
1721 case SCR_MAGIC_MAPPING
:
1726 case SCR_REMOVE_CURSE
:
1727 case SCR_TELEPORTATION
:
1731 case SCR_DETECT_CURSE
:
1741 case SCR_RANDOM_USELESSNESS
:
1745 case SCR_CURSE_ARMOUR
:
1746 case SCR_CURSE_WEAPON
:
1747 case SCR_CURSE_JEWELLERY
:
1748 case SCR_IMMOLATION
:
1756 if (item_known_cursed(item
))
1759 if (!item_type_known(item
))
1763 if (item_ident(item
, ISFLAG_KNOW_PLUSES
)
1764 && (item
.sub_type
== RING_PROTECTION
1765 || item
.sub_type
== RING_STRENGTH
1766 || item
.sub_type
== RING_EVASION
1767 || item
.sub_type
== RING_DEXTERITY
1768 || item
.sub_type
== RING_INTELLIGENCE
1769 || item
.sub_type
== RING_SLAYING
))
1772 valued
+= 10 * item
.plus
;
1774 if (item
.sub_type
== RING_SLAYING
&& item
.plus2
> 0)
1775 valued
+= 10 * item
.plus2
;
1780 if (item
.sub_type
== RING_SLAYING
&& item
.plus2
< 0)
1784 switch (item
.sub_type
)
1786 case RING_INVISIBILITY
:
1790 case RING_REGENERATION
:
1799 case RING_LIFE_PROTECTION
:
1803 case RING_TELEPORT_CONTROL
:
1807 case RING_MAGICAL_POWER
:
1808 case RING_PROTECTION_FROM_MAGIC
:
1816 case RING_LEVITATION
:
1817 case RING_POISON_RESISTANCE
:
1818 case RING_PROTECTION_FROM_COLD
:
1819 case RING_PROTECTION_FROM_FIRE
:
1824 case RING_SUSTAIN_ABILITIES
:
1825 case RING_SUSTENANCE
:
1826 case RING_TELEPORTATION
: // usually cursed
1830 case RING_SEE_INVISIBLE
:
1834 case RING_DEXTERITY
:
1836 case RING_INTELLIGENCE
:
1837 case RING_PROTECTION
:
1846 case AMU_THE_GOURMAND
:
1847 case AMU_GUARDIAN_SPIRIT
:
1853 case AMU_RESIST_CORROSION
:
1854 case AMU_RESIST_MUTATION
:
1859 case AMU_CONSERVATION
:
1860 case AMU_CONTROLLED_FLIGHT
:
1869 case AMU_INACCURACY
:
1872 // got to do delusion!
1875 if (is_artefact(item
))
1877 // in this branch we're guaranteed to know
1880 valued
= artefact_value(item
) - 5;
1882 valued
+= artefact_value(item
);
1889 case OBJ_MISCELLANY
:
1890 if (item_type_known(item
))
1892 switch (item
.sub_type
)
1894 case MISC_RUNE_OF_ZOT
: // upped from 1200 to encourage collecting
1898 case MISC_HORN_OF_GERYON
:
1902 case MISC_DISC_OF_STORMS
:
1906 case MISC_CRYSTAL_BALL_OF_SEEING
:
1910 case MISC_BOTTLED_EFREET
:
1914 case MISC_CRYSTAL_BALL_OF_FIXATION
:
1915 case MISC_EMPTY_EBONY_CASKET
:
1920 valued
+= 200 + item
.special
* 150;
1927 switch (item
.sub_type
)
1929 case MISC_RUNE_OF_ZOT
:
1933 case MISC_HORN_OF_GERYON
:
1937 case MISC_CRYSTAL_BALL_OF_SEEING
:
1941 case MISC_BOTTLED_EFREET
:
1954 if (item_type_known(item
))
1957 if (is_random_artefact(item
))
1959 // Consider spellbook as rare as the average of its
1960 // three rarest spells.
1961 int rarities
[SPELLBOOK_SIZE
];
1962 int count_valid
= 0;
1963 for (int i
= 0; i
< SPELLBOOK_SIZE
; i
++)
1965 spell_type spell
= which_spell_in_book(item
, i
);
1966 if (spell
== SPELL_NO_SPELL
)
1972 rarities
[i
] = spell_rarity(spell
);
1975 ASSERT(count_valid
> 0);
1977 if (count_valid
> 3)
1980 std::sort(rarities
, rarities
+ SPELLBOOK_SIZE
);
1981 for (int i
= SPELLBOOK_SIZE
- 1;
1982 i
>= SPELLBOOK_SIZE
- count_valid
; i
--)
1984 rarity
+= rarities
[i
];
1987 rarity
/= count_valid
;
1989 // Fixed level randarts get a bonus for the really low and
1990 // really high level spells.
1991 if (item
.sub_type
== BOOK_RANDART_LEVEL
)
1992 valued
+= 50 * abs(5 - item
.plus
);
1995 rarity
= book_rarity(item
.sub_type
);
1997 valued
+= (int)(rarity
* 50.0);
2002 if (!item_type_known(item
))
2004 else if (item
.sub_type
== STAFF_SMITING
2005 || item
.sub_type
== STAFF_STRIKING
2006 || item
.sub_type
== STAFF_WARDING
)
2013 if (item_is_rod(item
) && item_ident(item
, ISFLAG_KNOW_PLUSES
))
2014 valued
+= 50 * (item
.plus2
/ ROD_CHARGE_MULT
);
2028 valued
*= item
.quantity
;
2031 } // end item_value()
2033 static void _delete_shop(int i
)
2035 grd(you
.pos()) = DNGN_ABANDONED_SHOP
;
2036 unnotice_feature(level_pos(level_id::current(), you
.pos()));
2043 for (i
= 0; i
< MAX_SHOPS
; i
++)
2044 if (env
.shop
[i
].pos
== you
.pos())
2049 mpr("Help! Non-existent shop.", MSGCH_ERROR
);
2053 // Quick out, if no inventory
2054 if (_shop_get_stock(i
).empty())
2056 const shop_struct
& shop
= env
.shop
[i
];
2057 mprf("%s appears to be closed.", shop_name(shop
.pos
).c_str());
2062 int num_in_list
= 0;
2063 const bool bought_something
= _in_a_shop(i
, num_in_list
);
2064 const std::string shopname
= shop_name(env
.shop
[i
].pos
);
2066 // If the shop is now empty, erase it from the overview.
2067 if (_shop_get_stock(i
).empty())
2073 if (bought_something
)
2074 mprf("Thank you for shopping at %s!", shopname
.c_str());
2076 if (num_in_list
> 0)
2077 mpr("You can access your shopping list by pressing '$'.");
2080 void destroy_shop(shop_struct
*shop
)
2084 unnotice_feature(level_pos(level_id::current(), shop
->pos
));
2086 shop
->pos
= coord_def(0, 0);
2087 shop
->type
= SHOP_UNASSIGNED
;
2091 void destroy_shop_at(coord_def p
)
2093 destroy_shop(get_shop(p
));
2096 shop_struct
*get_shop(const coord_def
& where
)
2098 if (grd(where
) != DNGN_ENTER_SHOP
)
2101 unsigned short t
= env
.tgrid(where
);
2102 ASSERT(t
!= NON_ENTITY
&& t
< MAX_SHOPS
);
2103 ASSERT(env
.shop
[t
].pos
== where
&& env
.shop
[t
].type
!= SHOP_UNASSIGNED
);
2105 return (&env
.shop
[t
]);
2108 std::string
shop_name(const coord_def
& where
, bool add_stop
)
2110 std::string
name(shop_name(where
));
2116 std::string
shop_type_name (shop_type type
)
2120 case SHOP_WEAPON_ANTIQUE
:
2121 return "Antique Weapon";
2122 case SHOP_ARMOUR_ANTIQUE
:
2123 return "Antique Armour";
2128 case SHOP_JEWELLERY
:
2131 return "Magical Wand";
2137 return "Magic Scroll";
2138 case SHOP_GENERAL_ANTIQUE
:
2139 return "Assorted Antiques";
2140 case SHOP_DISTILLERY
:
2141 return "Distillery";
2143 return "General Store";
2149 std::string
shop_type_suffix (shop_type type
, const coord_def
&where
)
2151 if (type
== SHOP_GENERAL
2152 || type
== SHOP_GENERAL_ANTIQUE
2153 || type
== SHOP_DISTILLERY
)
2158 const char* suffixnames
[] = {"Shoppe", "Boutique", "Emporium", "Shop"};
2159 const int temp
= (where
.x
+ where
.y
) % 4;
2161 return std::string(suffixnames
[temp
]);
2164 std::string
shop_name(const coord_def
& where
)
2166 const shop_struct
*cshop
= get_shop(where
);
2168 // paranoia and shop mimics
2169 if (grd(where
) != DNGN_ENTER_SHOP
)
2171 if (monster_at(where
))
2173 monster
* mmimic
= monster_at(where
);
2174 if (mons_is_feat_mimic(mmimic
->type
) && mmimic
->props
.exists("shop_name"))
2175 return mmimic
->props
["shop_name"].get_string();
2183 mpr("Help! Non-existent shop.");
2184 return ("Buggy Shop");
2187 const shop_type type
= cshop
->type
;
2189 std::string sh_name
= "";
2191 if (!cshop
->shop_name
.empty())
2193 sh_name
+= apostrophise(cshop
->shop_name
) + " ";
2197 uint32_t seed
= static_cast<uint32_t>(cshop
->keeper_name
[0])
2198 | (static_cast<uint32_t>(cshop
->keeper_name
[1]) << 8)
2199 | (static_cast<uint32_t>(cshop
->keeper_name
[1]) << 16);
2201 sh_name
+= apostrophise(make_name(seed
, false)) + " ";
2204 if (!cshop
->shop_type_name
.empty())
2205 sh_name
+= cshop
->shop_type_name
;
2207 sh_name
+= shop_type_name(type
);
2209 if (!cshop
->shop_suffix_name
.empty())
2211 sh_name
+= " " + cshop
->shop_suffix_name
;
2215 std::string sh_suffix
= shop_type_suffix(type
, where
);
2216 if (!sh_suffix
.empty())
2217 sh_name
+= " " + sh_suffix
;
2223 bool is_shop_item(const item_def
&item
)
2225 return (item
.pos
.x
== 0 && item
.pos
.y
>= 5 && item
.pos
.y
< (MAX_SHOPS
+ 5));
2228 ////////////////////////////////////////////////////////////////////////
2230 // Setup shopping list after restoring savefile.
2231 static void _callback(bool saving
)
2234 shopping_list
.refresh();
2236 static SavefileCallback
_register_callback(_callback
);
2239 // * Let shopping list be modified from with the stash lister.
2240 // * Warn if buying something not on the shopping list would put
2241 // something on shopping list out of your reach.
2243 #define SHOPPING_LIST_KEY "shopping_list_key"
2244 #define SHOPPING_THING_COST_KEY "cost_key"
2245 #define SHOPPING_THING_ITEM_KEY "item_key"
2246 #define SHOPPING_THING_DESC_KEY "desc_key"
2247 #define SHOPPING_THING_VERB_KEY "verb_key"
2248 #define SHOPPING_THING_POS_KEY "pos_key"
2250 ShoppingList::ShoppingList()
2254 #define SETUP_POS() \
2257 mpr("SavefileCallback global constructor weirdness!", MSGCH_ERROR); \
2264 pos = level_pos::current(); \
2265 ASSERT(pos.is_valid());
2267 #define SETUP_THING() \
2268 CrawlHashTable *thing = new CrawlHashTable(); \
2269 (*thing)[SHOPPING_THING_COST_KEY] = cost; \
2270 (*thing)[SHOPPING_THING_POS_KEY] = pos;
2272 bool ShoppingList::add_thing(const item_def
&item
, int cost
,
2273 const level_pos
* _pos
)
2275 ASSERT(item
.defined());
2280 if (pos
.id
.level_type
!= LEVEL_DUNGEON
)
2282 mprf("The shopping list can only contain things in the dungeon.",
2287 if (find_thing(item
, pos
) != -1)
2289 mprf(MSGCH_ERROR
, "%s is already on the shopping list.",
2290 item
.name(DESC_CAP_THE
).c_str());
2295 (*thing
)[SHOPPING_THING_ITEM_KEY
] = item
;
2296 list
->push_back(*thing
);
2302 bool ShoppingList::add_thing(std::string desc
, std::string buy_verb
, int cost
,
2303 const level_pos
* _pos
)
2305 ASSERT(!desc
.empty());
2306 ASSERT(!buy_verb
.empty());
2311 if (pos
.id
.level_type
!= LEVEL_DUNGEON
)
2313 mprf("The shopping list can only contain things in the dungeon.",
2318 if (find_thing(desc
, pos
) != -1)
2320 mprf(MSGCH_ERROR
, "%s is already on the shopping list.",
2326 (*thing
)[SHOPPING_THING_DESC_KEY
] = desc
;
2327 (*thing
)[SHOPPING_THING_VERB_KEY
] = buy_verb
;
2328 list
->push_back(*thing
);
2336 bool ShoppingList::is_on_list(const item_def
&item
,
2337 const level_pos
* _pos
) const
2341 return (find_thing(item
, pos
) != -1);
2344 bool ShoppingList::is_on_list(std::string desc
,
2345 const level_pos
* _pos
) const
2349 return (find_thing(desc
, pos
) != -1);
2352 void ShoppingList::del_thing_at_index(int idx
)
2354 ASSERT(idx
>= 0 && idx
< list
->size());
2359 bool ShoppingList::del_thing(const item_def
&item
,
2360 const level_pos
* _pos
)
2364 int idx
= find_thing(item
, pos
);
2368 mprf(MSGCH_ERROR
, "%s isn't on shopping list, can't delete it.",
2369 item
.name(DESC_CAP_THE
).c_str());
2373 del_thing_at_index(idx
);
2377 bool ShoppingList::del_thing(std::string desc
, const level_pos
* _pos
)
2381 int idx
= find_thing(desc
, pos
);
2385 mprf(MSGCH_ERROR
, "%s isn't on shopping list, can't delete it.",
2390 del_thing_at_index(idx
);
2396 #define REMOVE_PROMPTED_KEY "remove_prompted_key"
2397 #define REPLACE_PROMPTED_KEY "replace_prompted_key"
2401 // * If you get a randart which lets you turn invisible, then remove
2402 // any ordinary rings of invisibility from the shopping list.
2404 // * If you collected enough spellbooks that all the spells in a
2405 // shopping list book are covered, then auto-remove it.
2406 unsigned int ShoppingList::cull_identical_items(const item_def
& item
,
2409 // Can't put items in Bazaar shops in the shopping list, so
2410 // don't bother transferring shopping list items to Bazaar shops.
2411 if (cost
!= -1 && you
.level_type
!= LEVEL_DUNGEON
)
2414 switch (item
.base_type
)
2419 // Only these are really interchangable.
2426 if (!item_type_known(item
) || is_artefact(item
))
2429 // Ignore stat-modification rings which reduce a stat, since they're
2431 if (item
.base_type
== OBJ_JEWELLERY
)
2433 if (item
.sub_type
== RING_SLAYING
&& item
.plus
< 0 && item
.plus2
< 0)
2440 // Item is already on shopping-list.
2441 const bool on_list
= find_thing(item
, level_pos::current()) != -1;
2443 const bool do_prompt
=
2444 (item
.base_type
== OBJ_JEWELLERY
&& !jewellery_is_amulet(item
)
2445 && ring_has_stackable_effect(item
))
2446 // Manuals and tomes of destruction are consumable.
2447 || (item
.base_type
== OBJ_BOOKS
2448 && (item
.sub_type
== BOOK_MANUAL
2449 || item
.sub_type
== BOOK_DESTRUCTION
));
2451 bool add_item
= false;
2453 std::vector
<level_pos
> to_del
;
2455 // NOTE: Don't modify the shopping list while iterating over it.
2456 for (unsigned int i
= 0; i
< list
->size(); i
++)
2458 CrawlHashTable
&thing
= (*list
)[i
];
2460 if (!thing_is_item(thing
))
2463 const item_def
& list_item
= get_thing_item(thing
);
2465 if (list_item
.base_type
!= item
.base_type
2466 || list_item
.sub_type
!= item
.sub_type
)
2471 if (!item_type_known(list_item
) || is_artefact(list_item
))
2474 const level_pos list_pos
= thing_pos(thing
);
2476 // cost = -1, we just found a shop item which is cheaper than
2477 // one on the shopping list.
2480 int list_cost
= thing_cost(thing
);
2482 if (cost
>= list_cost
)
2485 // Only prompt once.
2486 if (thing
.exists(REPLACE_PROMPTED_KEY
))
2488 thing
[REPLACE_PROMPTED_KEY
] = (bool) true;
2490 std::string prompt
=
2491 make_stringf("Shopping-list: replace %dgp %s with cheaper "
2492 "one? (Y/n)", list_cost
,
2493 describe_thing(thing
).c_str());
2495 if (_shop_yesno(prompt
.c_str(), 'y'))
2498 to_del
.push_back(list_pos
);
2503 // cost == -1, we just got an item which is on the shopping list.
2506 // Only prompt once.
2507 if (thing
.exists(REMOVE_PROMPTED_KEY
))
2509 thing
[REMOVE_PROMPTED_KEY
] = (bool) true;
2511 std::string prompt
=
2512 make_stringf("Shopping-list: remove %s? (Y/n)",
2513 describe_thing(thing
, DESC_NOCAP_A
).c_str());
2515 if (_shop_yesno(prompt
.c_str(), 'y'))
2516 to_del
.push_back(list_pos
);
2521 make_stringf("Shopping-list: removing %s",
2522 describe_thing(thing
, DESC_NOCAP_A
).c_str());
2524 _shop_mpr(str
.c_str());
2525 to_del
.push_back(list_pos
);
2529 for (unsigned int i
= 0; i
< to_del
.size(); i
++)
2530 del_thing(item
, &to_del
[i
]);
2532 if (add_item
&& !on_list
)
2533 add_thing(item
, cost
);
2535 return (to_del
.size());
2538 int ShoppingList::size() const
2542 mpr("SavefileCallback global constructor weirdness!", MSGCH_ERROR
);
2546 return (list
->size());
2549 bool ShoppingList::items_are_same(const item_def
& item_a
,
2550 const item_def
& item_b
)
2552 return (item_name_simple(item_a
) == item_name_simple(item_b
));
2555 void ShoppingList::move_things(const coord_def
&_src
, const coord_def
&_dst
)
2557 if (crawl_state
.map_stat_gen
|| crawl_state
.test
)
2558 // Shopping list is unitialized and uneeded.
2561 const level_pos
src(level_id::current(), _src
);
2562 const level_pos
dst(level_id::current(), _dst
);
2564 for (unsigned int i
= 0; i
< list
->size(); i
++)
2566 CrawlHashTable
&thing
= (*list
)[i
];
2568 if (thing_pos(thing
) == src
)
2569 thing
[SHOPPING_THING_POS_KEY
] = dst
;
2573 void ShoppingList::forget_pos(const level_pos
&pos
)
2575 if (!crawl_state
.need_save
)
2576 // Shopping list is unitialized and uneeded.
2579 for (unsigned int i
= 0; i
< list
->size(); i
++)
2581 const CrawlHashTable
&thing
= (*list
)[i
];
2583 if (thing_pos(thing
) == pos
)
2592 void ShoppingList::gold_changed(int old_amount
, int new_amount
)
2596 mpr("SavefileCallback global constructor weirdness!", MSGCH_ERROR
);
2600 if (new_amount
> old_amount
&& new_amount
>= min_unbuyable_cost
)
2602 ASSERT(min_unbuyable_idx
< list
->size());
2604 std::vector
<std::string
> descs
;
2605 for (unsigned int i
= min_unbuyable_idx
; i
< list
->size(); i
++)
2607 const CrawlHashTable
&thing
= (*list
)[i
];
2608 const int cost
= thing_cost(thing
);
2610 if (cost
> new_amount
)
2612 ASSERT(i
> (unsigned int) min_unbuyable_idx
);
2618 if (thing
.exists(SHOPPING_THING_VERB_KEY
))
2619 desc
+= thing
[SHOPPING_THING_VERB_KEY
].get_string();
2624 desc
+= describe_thing(thing
, DESC_NOCAP_A
);
2626 descs
.push_back(desc
);
2628 ASSERT(!descs
.empty());
2630 mpr_comma_separated_list("You now have enough gold to ", descs
,
2632 mpr("You can access your shopping list by pressing '$'.");
2634 // Reset max_buyable and min_unbuyable info
2637 else if (new_amount
< old_amount
&& new_amount
< max_buyable_cost
)
2639 // Reset max_buyable and min_unbuyable info
2644 class ShoppingListMenu
: public Menu
2655 void ShoppingListMenu::draw_title()
2659 const int total_cost
= you
.props
[SHOPPING_LIST_COST_KEY
];
2662 textcolor(title
->colour
);
2663 cprintf("%d %s%s, total cost %d gp",
2664 title
->quantity
, title
->text
.c_str(),
2665 title
->quantity
> 1? "s" : "",
2668 const char *verb
= menu_action
== ACT_EXECUTE
? "travel" :
2669 menu_action
== ACT_EXAMINE
? "examine" :
2671 draw_title_suffix(formatted_string::parse_string(make_stringf(
2672 "<lightgrey> [<w>a-z</w>: %s <w>?</w>/<w>!</w>: change action]",
2677 void ShoppingList::fill_out_menu(Menu
& shopmenu
)
2680 for (unsigned i
= 0; i
< list
->size(); ++i
, ++hotkey
)
2682 CrawlHashTable
&thing
= (*list
)[i
];
2683 level_pos pos
= thing_pos(thing
);
2684 int cost
= thing_cost(thing
);
2686 std::string etitle
=
2687 make_stringf("[%s] %s (%d gp)", short_place_name(pos
.id
).c_str(),
2688 name_thing(thing
, DESC_NOCAP_A
).c_str(),
2691 MenuEntry
*me
= new MenuEntry(etitle
, MEL_ITEM
, 1, hotkey
);
2694 if (cost
> you
.gold
)
2695 me
->colour
= DARKGREY
;
2696 else if (thing_is_item(thing
) && Options
.menu_colour_shops
)
2698 // Colour shopping list item according to menu colours.
2699 const item_def
&item
= get_thing_item(thing
);
2701 const std::string colprf
= menu_colour_item_prefix(item
);
2702 const int col
= menu_colour(item
.name(DESC_NOCAP_A
),
2709 shopmenu
.add_entry(me
);
2713 void ShoppingList::display()
2718 const bool travelable
= can_travel_interlevel();
2720 ShoppingListMenu shopmenu
;
2721 shopmenu
.set_tag("shop");
2722 shopmenu
.menu_action
= travelable
? Menu::ACT_EXECUTE
: Menu::ACT_EXAMINE
;
2723 shopmenu
.action_cycle
= travelable
? Menu::CYCLE_CYCLE
: Menu::CYCLE_NONE
;
2724 std::string title
= "thing";
2726 MenuEntry
*mtitle
= new MenuEntry(title
, MEL_TITLE
);
2727 // Abuse of the quantity field.
2728 mtitle
->quantity
= list
->size();
2729 shopmenu
.set_title(mtitle
);
2731 // Don't make a menu so tall that we recycle hotkeys on the same page.
2732 if (list
->size() > 52
2733 && (shopmenu
.maxpagesize() > 52 || shopmenu
.maxpagesize() == 0))
2735 shopmenu
.set_maxpagesize(52);
2738 std::string more_str
= make_stringf("<yellow>You have %d gp</yellow>",
2740 shopmenu
.set_more(formatted_string::parse_string(more_str
));
2742 shopmenu
.set_flags(MF_SINGLESELECT
| MF_ALWAYS_SHOW_MORE
2743 | MF_ALLOW_FORMATTING
);
2745 fill_out_menu(shopmenu
);
2747 std::vector
<MenuEntry
*> sel
;
2751 sel
= shopmenu
.show();
2756 const CrawlHashTable
* thing
=
2757 static_cast<const CrawlHashTable
*>(sel
[0]->data
);
2759 const bool is_item
= thing_is_item(*thing
);
2761 if (shopmenu
.menu_action
== Menu::ACT_EXECUTE
)
2763 const int cost
= thing_cost(*thing
);
2765 if (cost
> you
.gold
)
2767 std::string prompt
=
2768 make_stringf("You cannot afford %s; travel there "
2770 describe_thing(*thing
, DESC_NOCAP_A
).c_str());
2772 if (!yesno(prompt
.c_str(), true, 'n'))
2776 const travel_target
lp(thing_pos(*thing
), false);
2777 start_translevel_travel(lp
);
2780 else if (shopmenu
.menu_action
== Menu::ACT_EXAMINE
)
2785 const item_def
&item
= get_thing_item(*thing
);
2786 describe_item(const_cast<item_def
&>(item
));
2788 else // not an item, so we only stored a description.
2790 // HACK: Assume it's some kind of portal vault.
2791 snprintf(info
, INFO_SIZE
,
2792 "%s with an entry fee of %d gold pieces.",
2793 describe_thing(*thing
, DESC_CAP_A
).c_str(),
2794 (int) thing_cost(*thing
));
2796 print_description(info
);
2797 wait_for_keypress();
2800 else if (shopmenu
.menu_action
== Menu::ACT_MISC
)
2802 std::string prompt
=
2803 make_stringf("Delete %s from shopping list? (y/N)",
2804 describe_thing(*thing
, DESC_NOCAP_A
).c_str());
2806 if (!yesno(prompt
.c_str(), true, 'n'))
2809 const int index
= shopmenu
.get_entry_index(sel
[0]);
2812 mpr("ERROR: Unable to delete thing from shopping list!",
2818 del_thing_at_index(index
);
2819 if (list
->size() == 0)
2823 fill_out_menu(shopmenu
);
2826 die("Invalid menu action type");
2831 static bool _compare_shopping_things(const CrawlStoreValue
& a
,
2832 const CrawlStoreValue
& b
)
2834 const CrawlHashTable
& hash_a
= a
.get_table();
2835 const CrawlHashTable
& hash_b
= b
.get_table();
2837 const int a_cost
= hash_a
[SHOPPING_THING_COST_KEY
];
2838 const int b_cost
= hash_b
[SHOPPING_THING_COST_KEY
];
2840 return (a_cost
< b_cost
);
2843 void ShoppingList::refresh()
2845 if (!you
.props
.exists(SHOPPING_LIST_KEY
))
2846 you
.props
[SHOPPING_LIST_KEY
].new_vector(SV_HASH
, SFLAG_CONST_TYPE
);
2847 list
= &you
.props
[SHOPPING_LIST_KEY
].get_vector();
2849 std::sort(list
->begin(), list
->end(), _compare_shopping_things
);
2851 min_unbuyable_cost
= INT_MAX
;
2852 min_unbuyable_idx
= -1;
2853 max_buyable_cost
= -1;
2854 max_buyable_idx
= -1;
2858 for (unsigned int i
= 0; i
< list
->size(); i
++)
2860 const CrawlHashTable
&thing
= (*list
)[i
];
2862 const int cost
= thing_cost(thing
);
2864 if (cost
<= you
.gold
)
2866 max_buyable_cost
= cost
;
2867 max_buyable_idx
= i
;
2869 else if (min_unbuyable_idx
== -1)
2871 min_unbuyable_cost
= cost
;
2872 min_unbuyable_idx
= i
;
2876 you
.props
[SHOPPING_LIST_COST_KEY
].get_int() = total_cost
;
2879 int ShoppingList::find_thing(const item_def
&item
,
2880 const level_pos
&pos
) const
2882 for (unsigned int i
= 0; i
< list
->size(); i
++)
2884 const CrawlHashTable
&thing
= (*list
)[i
];
2885 const level_pos _pos
= thing_pos(thing
);
2890 if (!thing_is_item(thing
))
2893 const item_def
&_item
= get_thing_item(thing
);
2895 if (item_name_simple(item
) == item_name_simple(_item
))
2902 int ShoppingList::find_thing(const std::string
&desc
,
2903 const level_pos
&pos
) const
2905 for (unsigned int i
= 0; i
< list
->size(); i
++)
2907 const CrawlHashTable
&thing
= (*list
)[i
];
2908 const level_pos _pos
= thing_pos(thing
);
2913 if (thing_is_item(thing
))
2916 if (desc
== name_thing(thing
))
2923 bool ShoppingList::thing_is_item(const CrawlHashTable
& thing
)
2925 return thing
.exists(SHOPPING_THING_ITEM_KEY
);
2928 const item_def
& ShoppingList::get_thing_item(const CrawlHashTable
& thing
)
2930 ASSERT(thing
.exists(SHOPPING_THING_ITEM_KEY
));
2932 const item_def
&item
= thing
[SHOPPING_THING_ITEM_KEY
].get_item();
2933 ASSERT(item
.defined());
2938 std::string
ShoppingList::get_thing_desc(const CrawlHashTable
& thing
)
2940 ASSERT(thing
.exists(SHOPPING_THING_DESC_KEY
));
2942 std::string desc
= thing
[SHOPPING_THING_DESC_KEY
].get_string();
2946 int ShoppingList::thing_cost(const CrawlHashTable
& thing
)
2948 ASSERT(thing
.exists(SHOPPING_THING_COST_KEY
));
2949 return (thing
[SHOPPING_THING_COST_KEY
].get_int());
2952 level_pos
ShoppingList::thing_pos(const CrawlHashTable
& thing
)
2954 ASSERT(thing
.exists(SHOPPING_THING_POS_KEY
));
2955 return (thing
[SHOPPING_THING_POS_KEY
].get_level_pos());
2958 std::string
ShoppingList::name_thing(const CrawlHashTable
& thing
,
2959 description_level_type descrip
)
2961 if (thing_is_item(thing
))
2963 const item_def
&item
= get_thing_item(thing
);
2964 return item
.name(descrip
);
2968 std::string desc
= get_thing_desc(thing
);
2969 return apply_description(descrip
, desc
);
2973 std::string
ShoppingList::describe_thing(const CrawlHashTable
& thing
,
2974 description_level_type descrip
)
2976 const level_pos pos
= thing_pos(thing
);
2978 std::string desc
= name_thing(thing
, descrip
) + " on ";
2980 if (pos
.id
== level_id::current())
2981 desc
+= "this level";
2983 desc
+= pos
.id
.describe();
2988 // Item name without curse-status or inscription.
2989 std::string
ShoppingList::item_name_simple(const item_def
& item
, bool ident
)
2991 return item
.name(DESC_PLAIN
, false, ident
, false, false,