1 /**
2 * @file
3 * @brief Functions for inventory related commands.
4 **/
5
6 #include "AppHdr.h"
7
8 #include "invent.h"
9
10 #include <algorithm> // any_of
11 #include <cctype>
12 #include <cstdlib>
13 #include <cstring>
14 #include <iomanip>
15 #include <sstream>
16
17 #include "artefact.h"
18 #include "colour.h"
19 #include "command.h"
20 #include "describe.h"
21 #include "env.h"
22 #include "evoke.h"
23 #include "god-item.h"
24 #include "god-passive.h"
25 #include "initfile.h"
26 #include "item-prop.h"
27 #include "items.h"
28 #include "item-use.h"
29 #include "item-prop.h"
30 #include "item-status-flag-type.h"
31 #include "known-items.h"
32 #include "libutil.h"
33 #include "macro.h"
34 #include "message.h"
35 #include "options.h"
36 #include "output.h"
37 #include "prompt.h"
38 #include "religion.h"
39 #include "rltiles/tiledef-dngn.h"
40 #include "rltiles/tiledef-icons.h"
41 #include "rltiles/tiledef-main.h"
42 #include "showsymb.h"
43 #include "state.h"
44 #include "stringutil.h"
45 #include "tag-version.h"
46 #include "terrain.h"
47 #include "throw.h"
48 #include "tilepick.h"
49 #include "tile-env.h"
50
51 ///////////////////////////////////////////////////////////////////////////////
52 // Inventory menu shenanigans
53
54 static void _get_inv_items_to_show(vector<const item_def*> &v,
55 int selector, int excluded_slot = -1);
56
InvTitle(Menu * mn,const string & title,invtitle_annotator tfn)57 InvTitle::InvTitle(Menu *mn, const string &title, invtitle_annotator tfn)
58 : MenuEntry(title, MEL_TITLE)
59 {
60 m = mn;
61 titlefn = tfn;
62 }
63
get_text(const bool) const64 string InvTitle::get_text(const bool) const
65 {
66 return titlefn ? titlefn(m, MenuEntry::get_text())
67 : MenuEntry::get_text();
68 }
69
InvEntry(const item_def & i)70 InvEntry::InvEntry(const item_def &i)
71 : MenuEntry("", MEL_ITEM), item(&i), _has_star(false)
72 {
73 // Data is an inherited void *. When using InvEntry in menus
74 // use the const item in this class whenever possible
75 data = const_cast<item_def *>(item);
76
77 if (in_inventory(i) && i.base_type != OBJ_GOLD)
78 {
79 // We need to do this in order to get the 'wielded' annotation.
80 // We then toss out the first four characters, which look
81 // like this: "a - ". Ow. FIXME.
82 text = i.name(DESC_INVENTORY_EQUIP, false).substr(4);
83 }
84 else
85 text = i.name(DESC_A, false);
86
87 if (item_is_stationary_net(i))
88 {
89 actor *trapped = actor_at(i.pos);
90 text += make_stringf(" (holding %s)",
91 trapped ? trapped->name(DESC_A).c_str()
92 : "nobody"); // buggy net, but don't crash
93 }
94
95 if (i.base_type != OBJ_GOLD && in_inventory(i))
96 add_hotkey(index_to_letter(i.link));
97 else
98 add_hotkey(' '); // dummy hotkey
99
100 add_class_hotkeys(i);
101
102 quantity = i.quantity;
103 }
104
get_basename() const105 const string &InvEntry::get_basename() const
106 {
107 if (basename.empty())
108 basename = item->name(DESC_BASENAME);
109 return basename;
110 }
111
get_qualname() const112 const string &InvEntry::get_qualname() const
113 {
114 if (qualname.empty())
115 qualname = item->name(DESC_QUALNAME);
116 return qualname;
117 }
118
get_fullname() const119 const string &InvEntry::get_fullname() const
120 {
121 return text;
122 }
123
get_dbname() const124 const string &InvEntry::get_dbname() const
125 {
126 if (dbname.empty())
127 dbname = item->name(DESC_DBNAME);
128 return dbname;
129 }
130
is_cursed() const131 bool InvEntry::is_cursed() const
132 {
133 return item->cursed();
134 }
135
is_glowing() const136 bool InvEntry::is_glowing() const
137 {
138 return !item_ident(*item, ISFLAG_KNOW_TYPE)
139 && (get_equip_desc(*item)
140 || (is_artefact(*item)
141 && (item->base_type == OBJ_WEAPONS
142 || item->base_type == OBJ_ARMOUR
143 || item->base_type == OBJ_BOOKS)));
144 }
145
is_ego() const146 bool InvEntry::is_ego() const
147 {
148 return item_ident(*item, ISFLAG_KNOW_TYPE) && !is_artefact(*item)
149 && item->brand != 0
150 && (item->base_type == OBJ_WEAPONS
151 || item->base_type == OBJ_MISSILES
152 || item->base_type == OBJ_ARMOUR);
153 }
154
is_art() const155 bool InvEntry::is_art() const
156 {
157 return item_ident(*item, ISFLAG_KNOW_TYPE) && is_artefact(*item);
158 }
159
is_equipped() const160 bool InvEntry::is_equipped() const
161 {
162 if (item->link == -1 || item->pos != ITEM_IN_INVENTORY)
163 return false;
164
165 for (int i = EQ_FIRST_EQUIP; i < NUM_EQUIP; i++)
166 if (item->link == you.equip[i])
167 return true;
168
169 return false;
170 }
171
select(int qty)172 void InvEntry::select(int qty)
173 {
174 if (item && item->quantity < qty)
175 qty = item->quantity;
176
177 MenuEntry::select(qty);
178 }
179
has_star() const180 bool InvEntry::has_star() const
181 {
182 return _has_star;
183 }
184
get_filter_text() const185 string InvEntry::get_filter_text() const
186 {
187 return item_prefix(*item, false) + " " + get_text();
188 }
189
get_text(bool need_cursor) const190 string InvEntry::get_text(bool need_cursor) const
191 {
192 need_cursor = need_cursor && show_cursor;
193
194 ostringstream tstr;
195
196 const bool nosel = hotkeys.empty();
197 const char key = nosel ? ' ' : static_cast<char>(hotkeys[0]);
198
199 tstr << ' ';
200
201 if (!nosel || tag == "pickup")
202 {
203 tstr << key;
204
205 if (need_cursor)
206 tstr << '[';
207 else
208 tstr << ' ';
209
210 if (nosel)
211 tstr << ' ';
212 else if (!selected_qty)
213 tstr << '-';
214 else if (selected_qty < quantity)
215 tstr << '#';
216 else if (_has_star)
217 tstr << '*';
218 else
219 tstr << '+';
220
221 if (need_cursor)
222 tstr << ']';
223 else
224 tstr << ' ';
225 }
226 if (InvEntry::show_glyph)
227 tstr << "(" << glyph_to_tagstr(get_item_glyph(*item)) << ")" << " ";
228
229 if (InvEntry::show_coordinates && in_bounds(item->pos))
230 {
231 const coord_def relpos = item->pos - you.pos();
232 tstr << "(" << relpos.x << ", " << -relpos.y << ")" << " ";
233 }
234
235 tstr << text;
236 return tstr.str();
237 }
238
get_class_hotkeys(const int type,vector<char> & glyphs)239 void get_class_hotkeys(const int type, vector<char> &glyphs)
240 {
241 switch (type)
242 {
243 case OBJ_GOLD:
244 glyphs.push_back('$');
245 break;
246 case OBJ_MISSILES:
247 glyphs.push_back('(');
248 break;
249 case OBJ_WEAPONS:
250 glyphs.push_back(')');
251 break;
252 case OBJ_ARMOUR:
253 glyphs.push_back('[');
254 break;
255 case OBJ_WANDS:
256 glyphs.push_back('/');
257 break;
258 case OBJ_BOOKS:
259 glyphs.push_back(':');
260 break;
261 case OBJ_SCROLLS:
262 glyphs.push_back('?');
263 break;
264 case OBJ_JEWELLERY:
265 glyphs.push_back('"');
266 glyphs.push_back('=');
267 break;
268 case OBJ_POTIONS:
269 glyphs.push_back('!');
270 break;
271 case OBJ_STAVES:
272 glyphs.push_back('|');
273 break;
274 #if TAG_MAJOR_VERSION == 34
275 case OBJ_RODS:
276 glyphs.push_back('\\');
277 break;
278 #endif
279 case OBJ_MISCELLANY:
280 glyphs.push_back('}');
281 break;
282 default:
283 break;
284 }
285 }
286
add_class_hotkeys(const item_def & i)287 void InvEntry::add_class_hotkeys(const item_def &i)
288 {
289 const int type = i.base_type;
290 if (type == OBJ_JEWELLERY)
291 {
292 add_hotkey(i.sub_type >= AMU_FIRST_AMULET ? '"' : '=');
293 return;
294 }
295
296 vector<char> glyphs;
297 get_class_hotkeys(type, glyphs);
298 for (char gly : glyphs)
299 add_hotkey(gly);
300 }
301
302 bool InvEntry::show_cursor = false;
set_show_cursor(bool doshow)303 void InvEntry::set_show_cursor(bool doshow)
304 {
305 show_cursor = doshow;
306 }
307
308 bool InvEntry::show_glyph = false;
set_show_glyph(bool doshow)309 void InvEntry::set_show_glyph(bool doshow)
310 {
311 show_glyph = doshow;
312 }
313
314 bool InvEntry::show_coordinates = false;
set_show_coordinates(bool doshow)315 void InvEntry::set_show_coordinates(bool doshow)
316 {
317 show_coordinates = doshow;
318 }
319
InvMenu(int mflags)320 InvMenu::InvMenu(int mflags)
321 : Menu(mflags, "inventory"), type(menu_type::invlist), pre_select(nullptr),
322 title_annotate(nullptr), _mode_special_drop(false)
323 {
324 #ifdef USE_TILE_LOCAL
325 if (Options.tile_menu_icons)
326 set_flags(mflags | MF_USE_TWO_COLUMNS);
327 #endif
328
329 InvEntry::set_show_cursor(false);
330 }
331
mode_special_drop() const332 bool InvMenu::mode_special_drop() const
333 {
334 return _mode_special_drop;
335 }
336
set_type(menu_type t)337 void InvMenu::set_type(menu_type t)
338 {
339 type = t;
340 }
341
set_title_annotator(invtitle_annotator afn)342 void InvMenu::set_title_annotator(invtitle_annotator afn)
343 {
344 title_annotate = afn;
345 }
346
set_title(MenuEntry * t,bool first)347 void InvMenu::set_title(MenuEntry *t, bool first)
348 {
349 Menu::set_title(t, first);
350 }
351
set_preselect(const vector<SelItem> * pre)352 void InvMenu::set_preselect(const vector<SelItem> *pre)
353 {
354 pre_select = pre;
355 }
356
slot_description()357 string slot_description()
358 {
359 return make_stringf("%d/%d slots", inv_count(), ENDOFPACK);
360 }
361
set_title(const string & s)362 void InvMenu::set_title(const string &s)
363 {
364 set_title(new InvTitle(this, s.empty() ? "Inventory: " + slot_description()
365 : s,
366 title_annotate));
367 }
368
pre_process(int key)369 int InvMenu::pre_process(int key)
370 {
371 if (type == menu_type::drop && key == '\\')
372 {
373 _mode_special_drop = !_mode_special_drop;
374 key = CK_NO_KEY;
375 }
376 else if (key == ';'
377 && you.last_unequip != -1
378 && (type == menu_type::drop || type == menu_type::invlist))
379 {
380 key = index_to_letter(you.last_unequip);
381 }
382 else if (key == '-')
383 _mode_special_drop = false;
384 return key;
385 }
386
_item_is_permadrop_candidate(const item_def & item)387 static bool _item_is_permadrop_candidate(const item_def &item)
388 {
389 // Known, non-artefact items of the types you see on the '\' menu proper.
390 // (No disabling autopickup for "green fizzy potion", "+3 whip", etc.)
391 if (item_type_unknown(item))
392 return false;
393 return item.base_type == OBJ_MISCELLANY
394 || is_stackable_item(item)
395 || item_type_has_ids(item.base_type);
396 }
397
select_item_index(int idx,int qty,bool draw_cursor)398 void InvMenu::select_item_index(int idx, int qty, bool draw_cursor)
399 {
400 if (type != menu_type::drop)
401 return Menu::select_item_index(idx, qty, draw_cursor);
402
403 InvEntry *ie = static_cast<InvEntry*>(items[idx]);
404
405 bool should_toggle_star = _item_is_permadrop_candidate(ie->item[0])
406 && (ie->has_star() || _mode_special_drop);
407
408 if (should_toggle_star)
409 {
410 // Toggle starred items back to selected-but-not-starred in this mode
411 // instead of turning them all the way off.
412 qty = _mode_special_drop ? -2 : 0;
413 ie->set_star(!ie->has_star());
414 }
415 Menu::select_item_index(idx, qty, draw_cursor);
416 }
417
set_star(bool val)418 void InvEntry::set_star(bool val)
419 {
420 _has_star = val;
421 }
422
_has_melded_armour()423 static bool _has_melded_armour()
424 {
425 for (int e = EQ_CLOAK; e <= EQ_BODY_ARMOUR; e++)
426 if (you.melded[e])
427 return true;
428 return false;
429 }
430
_has_temp_unwearable_armour()431 static bool _has_temp_unwearable_armour()
432 {
433 for (const auto &item : you.inv)
434 {
435 if (item.defined() && item.base_type == OBJ_ARMOUR
436 && can_wear_armour(item, false, true)
437 && !can_wear_armour(item, false, false))
438 {
439 return true;
440 }
441 }
442 return false;
443 }
444
_has_hand_evokable()445 static bool _has_hand_evokable()
446 {
447 for (const auto &item : you.inv)
448 {
449 if (item.defined()
450 && item_is_evokable(item, true, false, false)
451 && !item_is_evokable(item, true, false, true))
452 {
453 return true;
454 }
455 }
456 return false;
457 }
458
459 /**
460 * What message should the player be given when they look for items matching
461 * the given selector and don't find any?
462 *
463 * @param selector The given type of object_selector.
464 * @return A message such as "You aren't carrying any weapons."
465 * "Your armour is currently melded into you.", etc.
466 */
no_selectables_message(int item_selector)467 string no_selectables_message(int item_selector)
468 {
469 switch (item_selector)
470 {
471 case OSEL_ANY:
472 return "You aren't carrying anything.";
473 case OSEL_WIELD:
474 case OBJ_WEAPONS:
475 return "You aren't carrying any weapons.";
476 case OSEL_BLESSABLE_WEAPON:
477 return "You aren't carrying any weapons that can be blessed.";
478 case OBJ_ARMOUR:
479 {
480 if (_has_melded_armour())
481 return "Your armour is currently melded into you.";
482 else if (_has_temp_unwearable_armour())
483 return "You aren't carrying any currently wearable armour.";
484 else
485 return "You aren't carrying any wearable armour.";
486 }
487 case OSEL_UNIDENT:
488 return "You don't have any unidentified items.";
489 case OSEL_ENCHANTABLE_ARMOUR:
490 return "You aren't carrying any armour which can be enchanted further.";
491 case OBJ_CORPSES:
492 return "You don't have any corpses.";
493 case OBJ_POTIONS:
494 return "You aren't carrying any potions.";
495 case OBJ_SCROLLS:
496 return "You aren't carrying any scrolls.";
497 case OBJ_BOOKS:
498 return "You don't have any books.";
499 case OBJ_WANDS:
500 return "You aren't carrying any wands.";
501 case OBJ_JEWELLERY:
502 return "You aren't carrying any pieces of jewellery.";
503 case OSEL_THROWABLE:
504 return "You aren't carrying any items that might be thrown or fired.";
505 case OSEL_EVOKABLE:
506 if (_has_hand_evokable())
507 return "You aren't carrying any items that you can evoke without wielding.";
508 else
509 return "You aren't carrying any items that you can evoke.";
510 case OSEL_CURSED_WORN:
511 return "None of your equipped items are cursed.";
512 #if TAG_MAJOR_VERSION == 34
513 case OSEL_UNCURSED_WORN_ARMOUR:
514 return "You aren't wearing any piece of uncursed armour.";
515 case OSEL_UNCURSED_WORN_JEWELLERY:
516 return "You aren't wearing any piece of uncursed jewellery.";
517 #endif
518 case OSEL_BRANDABLE_WEAPON:
519 return "You aren't carrying any weapons that can be branded.";
520 case OSEL_ENCHANTABLE_WEAPON:
521 return "You aren't carrying any weapons that can be enchanted.";
522 case OSEL_BEOGH_GIFT:
523 return "You aren't carrying anything you can give to a follower.";
524 case OSEL_CURSABLE:
525 return "You aren't wearing any cursable items.";
526 case OSEL_UNCURSED_WORN_RINGS:
527 return "You aren't wearing any uncursed rings.";
528 case OSEL_QUIVER_ACTION:
529 case OSEL_QUIVER_ACTION_FORCE:
530 return "You don't have any quiverable items.";
531 }
532
533 return "You aren't carrying any such object.";
534 }
535
load_inv_items(int item_selector,int excluded_slot,function<MenuEntry * (MenuEntry *)> procfn)536 void InvMenu::load_inv_items(int item_selector, int excluded_slot,
537 function<MenuEntry* (MenuEntry*)> procfn)
538 {
539 vector<const item_def *> tobeshown;
540 _get_inv_items_to_show(tobeshown, item_selector, excluded_slot);
541
542 load_items(tobeshown, procfn);
543
544 if (!item_count())
545 set_title(no_selectables_message(item_selector));
546 else
547 set_title("");
548 }
549
get_tiles_for_item(const item_def & item,vector<tile_def> & tileset,bool show_background)550 bool get_tiles_for_item(const item_def &item, vector<tile_def>& tileset, bool show_background)
551 {
552 tileidx_t idx = tileidx_item(get_item_known_info(item));
553 if (!idx)
554 return false;
555
556 if (in_inventory(item))
557 {
558 const equipment_type eq = item_equip_slot(item);
559 if (eq != EQ_NONE)
560 {
561 if (item.cursed())
562 tileset.emplace_back(TILE_ITEM_SLOT_EQUIP_CURSED);
563 else
564 tileset.emplace_back(TILE_ITEM_SLOT_EQUIP);
565 }
566 else if (item.cursed())
567 tileset.emplace_back(TILE_ITEM_SLOT_CURSED);
568
569 tileidx_t base_item = tileidx_known_base_item(idx);
570 if (base_item)
571 tileset.emplace_back(base_item);
572 tileset.emplace_back(idx);
573
574 if (eq != EQ_NONE && you.melded[eq])
575 tileset.emplace_back(TILEI_MESH);
576 }
577 else
578 {
579 // Do we want to display the floor type or is that too distracting?
580 const coord_def c = item.held_by_monster()
581 ? item.holding_monster()->pos()
582 : item.pos;
583 tileidx_t ch = 0;
584 if (c != coord_def() && show_background && item.link != ITEM_IN_SHOP)
585 {
586 ch = tileidx_feature(c);
587 if (ch == TILE_FLOOR_NORMAL)
588 ch = tile_env.flv(c).floor;
589 else if (ch == TILE_WALL_NORMAL)
590 ch = tile_env.flv(c).wall;
591
592 tileset.emplace_back(ch);
593 }
594 tileidx_t base_item = tileidx_known_base_item(idx);
595 if (base_item)
596 tileset.emplace_back(base_item);
597
598 tileset.emplace_back(idx);
599
600 if (ch != 0)
601 {
602 // Needs to be displayed so as to not give away mimics in shallow water.
603 if (ch == TILE_DNGN_SHALLOW_WATER)
604 tileset.emplace_back(TILEI_MASK_SHALLOW_WATER);
605 else if (ch == TILE_DNGN_SHALLOW_WATER_MURKY)
606 tileset.emplace_back(TILEI_MASK_SHALLOW_WATER_MURKY);
607 }
608 }
609 if (item.base_type == OBJ_WEAPONS || item.base_type == OBJ_MISSILES
610 || item.base_type == OBJ_ARMOUR
611 #if TAG_MAJOR_VERSION == 34
612 || item.base_type == OBJ_RODS
613 #endif
614 )
615 {
616 tileidx_t brand = tileidx_known_brand(item);
617 if (brand)
618 tileset.emplace_back(brand);
619 }
620 else if (item.base_type == OBJ_CORPSES)
621 {
622 tileidx_t brand = tileidx_corpse_brand(item);
623 if (brand)
624 tileset.emplace_back(brand);
625 }
626
627 return true;
628 }
629
630 #ifdef USE_TILE
get_tiles(vector<tile_def> & tileset) const631 bool InvEntry::get_tiles(vector<tile_def>& tileset) const
632 {
633 if (!Options.tile_menu_icons)
634 return false;
635
636 // Runes + orb of zot have a special uncollected tile
637 if (quantity <= 0 && (item->base_type != OBJ_RUNES && item->base_type != OBJ_ORBS))
638 return false;
639
640 return get_tiles_for_item(*item, tileset, show_background);
641 }
642 #else
get_tiles(vector<tile_def> &) const643 bool InvEntry::get_tiles(vector<tile_def>& /*tileset*/) const { return false; }
644 #endif
645
is_selectable(int index) const646 bool InvMenu::is_selectable(int index) const
647 {
648 if (type == menu_type::drop)
649 {
650 InvEntry *item = dynamic_cast<InvEntry*>(items[index]);
651 if (item->is_cursed() && item->is_equipped())
652 return false;
653
654 string text = item->get_text();
655
656 if (text.find("!*") != string::npos || text.find("!d") != string::npos)
657 return false;
658 }
659
660 return Menu::is_selectable(index);
661 }
662
663 template <const string &(InvEntry::*method)() const>
compare_item_str(const InvEntry * a,const InvEntry * b)664 static int compare_item_str(const InvEntry *a, const InvEntry *b)
665 {
666 return (a->*method)().compare((b->*method)());
667 }
668
669 // Would call this just compare_item, but MSVC mistakenly thinks the next
670 // one is a specialization rather than an overload.
671 template <typename T, T (*proc)(const InvEntry *a)>
compare_item_fn(const InvEntry * a,const InvEntry * b)672 static int compare_item_fn(const InvEntry *a, const InvEntry *b)
673 {
674 return int(proc(a)) - int(proc(b));
675 }
676
677 template <typename T, T (InvEntry::*method)() const>
compare_item(const InvEntry * a,const InvEntry * b)678 static int compare_item(const InvEntry *a, const InvEntry *b)
679 {
680 return int((a->*method)()) - int((b->*method)());
681 }
682
683 template <typename T, T (InvEntry::*method)() const>
compare_item_rev(const InvEntry * a,const InvEntry * b)684 static int compare_item_rev(const InvEntry *a, const InvEntry *b)
685 {
686 return int((b->*method)()) - int((a->*method)());
687 }
688
689 template <item_sort_fn cmp>
compare_reverse(const InvEntry * a,const InvEntry * b)690 static int compare_reverse(const InvEntry *a, const InvEntry *b)
691 {
692 return -cmp(a, b);
693 }
694
695 // We need C++11 already!
696 // Some prototypes to prevent warnings; we can't make these static because
697 // they're used as template parameters.
698 int sort_item_qty(const InvEntry *a);
699 int sort_item_slot(const InvEntry *a);
700 bool sort_item_identified(const InvEntry *a);
701 bool sort_item_charged(const InvEntry *a);
702
sort_item_qty(const InvEntry * a)703 int sort_item_qty(const InvEntry *a)
704 {
705 return a->quantity;
706 }
sort_item_slot(const InvEntry * a)707 int sort_item_slot(const InvEntry *a)
708 {
709 return a->item->link;
710 }
711
sort_item_identified(const InvEntry * a)712 bool sort_item_identified(const InvEntry *a)
713 {
714 return !item_type_known(*(a->item));
715 }
716
sort_item_charged(const InvEntry * a)717 bool sort_item_charged(const InvEntry *a)
718 {
719 return a->item->base_type != OBJ_WANDS
720 || !item_is_evokable(*(a->item), false);
721 }
722
_compare_invmenu_items(const InvEntry * a,const InvEntry * b,const item_sort_comparators * cmps)723 static bool _compare_invmenu_items(const InvEntry *a, const InvEntry *b,
724 const item_sort_comparators *cmps)
725 {
726 for (const auto &comparator : *cmps)
727 {
728 const int cmp = comparator.compare(a, b);
729 if (cmp)
730 return cmp < 0;
731 }
732 return a->item->link < b->item->link;
733 }
734
735 struct menu_entry_comparator
736 {
737 const menu_sort_condition *cond;
738
menu_entry_comparatormenu_entry_comparator739 menu_entry_comparator(const menu_sort_condition *c)
740 : cond(c)
741 {
742 }
743
operator ()menu_entry_comparator744 bool operator () (const MenuEntry* a, const MenuEntry* b) const
745 {
746 const InvEntry *ia = dynamic_cast<const InvEntry *>(a);
747 const InvEntry *ib = dynamic_cast<const InvEntry *>(b);
748 return _compare_invmenu_items(ia, ib, &cond->cmp);
749 }
750 };
751
init_item_sort_comparators(item_sort_comparators & list,const string & set)752 void init_item_sort_comparators(item_sort_comparators &list, const string &set)
753 {
754 static struct
755 {
756 const string cname;
757 item_sort_fn cmp;
758 } cmp_map[] =
759 {
760 { "basename", compare_item_str<&InvEntry::get_basename> },
761 { "qualname", compare_item_str<&InvEntry::get_qualname> },
762 { "fullname", compare_item_str<&InvEntry::get_fullname> },
763 { "dbname", compare_item_str<&InvEntry::get_dbname> },
764 { "curse", compare_item<bool, &InvEntry::is_cursed> },
765 { "glowing", compare_item_rev<bool, &InvEntry::is_glowing> },
766 { "ego", compare_item_rev<bool, &InvEntry::is_ego> },
767 { "art", compare_item_rev<bool, &InvEntry::is_art> },
768 { "equipped", compare_item_rev<bool, &InvEntry::is_equipped> },
769 { "identified",compare_item_fn<bool, sort_item_identified> },
770 { "charged", compare_item_fn<bool, sort_item_charged>},
771 { "qty", compare_item_fn<int, sort_item_qty> },
772 { "slot", compare_item_fn<int, sort_item_slot> },
773 };
774
775 list.clear();
776 for (string s : split_string(",", set))
777 {
778 if (s.empty())
779 continue;
780
781 const bool negated = s[0] == '>';
782 if (s[0] == '<' || s[0] == '>')
783 s = s.substr(1);
784
785 for (const auto &ci : cmp_map)
786 if (ci.cname == s)
787 {
788 list.emplace_back(ci.cmp, negated);
789 break;
790 }
791 }
792
793 if (list.empty())
794 list.emplace_back(compare_item_str<&InvEntry::get_fullname>);
795 }
796
find_menu_sort_condition() const797 const menu_sort_condition *InvMenu::find_menu_sort_condition() const
798 {
799 for (int i = Options.sort_menus.size() - 1; i >= 0; --i)
800 if (Options.sort_menus[i].matches(type))
801 return &Options.sort_menus[i];
802
803 return nullptr;
804 }
805
sort_menu(vector<InvEntry * > & invitems,const menu_sort_condition * cond)806 void InvMenu::sort_menu(vector<InvEntry*> &invitems,
807 const menu_sort_condition *cond)
808 {
809 if (!cond || cond->sort == -1 || (int) invitems.size() < cond->sort)
810 return;
811
812 sort(invitems.begin(), invitems.end(), menu_entry_comparator(cond));
813 }
814
815 FixedVector<int, NUM_OBJECT_CLASSES> inv_order(
816 OBJ_WEAPONS,
817 OBJ_MISSILES,
818 OBJ_ARMOUR,
819 OBJ_STAVES,
820 #if TAG_MAJOR_VERSION == 34
821 OBJ_RODS,
822 #endif
823 OBJ_JEWELLERY,
824 OBJ_WANDS,
825 OBJ_SCROLLS,
826 OBJ_POTIONS,
827 OBJ_BOOKS,
828 OBJ_MISCELLANY,
829 #if TAG_MAJOR_VERSION == 34
830 OBJ_FOOD,
831 #endif
832 // These four can't actually be in your inventory.
833 OBJ_CORPSES,
834 OBJ_RUNES,
835 OBJ_ORBS,
836 OBJ_GOLD);
837
load_items(const vector<item_def> & mitems,function<MenuEntry * (MenuEntry *)> procfn,menu_letter ckey,bool sort)838 menu_letter InvMenu::load_items(const vector<item_def>& mitems,
839 function<MenuEntry* (MenuEntry*)> procfn,
840 menu_letter ckey, bool sort)
841 {
842 vector<const item_def*> xlatitems;
843 for (const item_def &item : mitems)
844 xlatitems.push_back(&item);
845 return load_items(xlatitems, procfn, ckey, sort);
846 }
847
load_items(const vector<const item_def * > & mitems,function<MenuEntry * (MenuEntry *)> procfn,menu_letter ckey,bool sort)848 menu_letter InvMenu::load_items(const vector<const item_def*> &mitems,
849 function<MenuEntry* (MenuEntry*)> procfn,
850 menu_letter ckey, bool sort)
851 {
852 FixedVector< int, NUM_OBJECT_CLASSES > inv_class(0);
853 for (const item_def * const mitem : mitems)
854 inv_class[mitem->base_type]++;
855
856 vector<InvEntry*> items_in_class;
857 const menu_sort_condition *cond = nullptr;
858 if (sort) cond = find_menu_sort_condition();
859
860 for (int obj = 0; obj < NUM_OBJECT_CLASSES; ++obj)
861 {
862 int i = inv_order[obj];
863
864 if (!inv_class[i])
865 continue;
866
867 string subtitle = item_class_name(i);
868
869 // Mention the class selection shortcuts.
870 if (is_set(MF_MULTISELECT))
871 {
872 vector<char> glyphs;
873 get_class_hotkeys(i, glyphs);
874 if (!glyphs.empty())
875 {
876 // longest string
877 const string str = "Magical Staves ";
878 subtitle += string(strwidth(str) - strwidth(subtitle),
879 ' ');
880 subtitle += "(select all with <w>";
881 for (char gly : glyphs)
882 subtitle += gly;
883 subtitle += "</w><blue>)";
884 }
885 }
886 add_entry(new MenuEntry(subtitle, MEL_SUBTITLE));
887
888 items_in_class.clear();
889
890 InvEntry *forced_first = nullptr;
891 for (const item_def * const mitem : mitems)
892 {
893 if (mitem->base_type != i)
894 continue;
895
896 InvEntry * const ie = new InvEntry(*mitem);
897 if (mitem->sub_type == get_max_subtype(mitem->base_type))
898 forced_first = ie;
899 else
900 items_in_class.push_back(ie);
901 }
902
903 sort_menu(items_in_class, cond);
904 if (forced_first)
905 items_in_class.insert(items_in_class.begin(),forced_first);
906
907 for (InvEntry *ie : items_in_class)
908 {
909 if (tag == "pickup")
910 {
911 if (ie->item && item_is_stationary(*ie->item))
912 ie->tag = "nopickup";
913 else
914 ie->tag = "pickup";
915 }
916 if (get_flags() & MF_NOSELECT)
917 ie->hotkeys.clear();
918 // If there's no hotkey, provide one.
919 else if (ie->hotkeys[0] == ' ')
920 {
921 if (ie->tag == "nopickup")
922 ie->hotkeys.clear();
923 else
924 ie->hotkeys[0] = ckey++;
925 }
926 do_preselect(ie);
927
928 add_entry(procfn ? procfn(ie) : ie);
929 }
930 }
931
932 return ckey;
933 }
934
do_preselect(InvEntry * ie)935 void InvMenu::do_preselect(InvEntry *ie)
936 {
937 if (!pre_select || pre_select->empty())
938 return;
939
940 for (const SelItem &presel : *pre_select)
941 if (ie->item && ie->item == presel.item)
942 {
943 ie->selected_qty = presel.quantity;
944 break;
945 }
946 }
947
get_selitems() const948 vector<SelItem> InvMenu::get_selitems() const
949 {
950 vector<SelItem> selected_items;
951 for (MenuEntry *me : sel)
952 {
953 InvEntry *inv = dynamic_cast<InvEntry*>(me);
954 selected_items.emplace_back(inv->hotkeys[0], inv->selected_qty,
955 inv->item, inv->has_star());
956 }
957 return selected_items;
958 }
959
help_key() const960 string InvMenu::help_key() const
961 {
962 return type == menu_type::drop || type == menu_type::pickup ? "pick-up"
963 : "";
964 }
965
getkey() const966 int InvMenu::getkey() const
967 {
968 auto mkey = lastch;
969 if (type == menu_type::know && (mkey == 0 || mkey == CK_ENTER))
970 return mkey;
971
972 if (!isaalnum(mkey) && mkey != '$' && mkey != '-' && mkey != '?'
973 && mkey != '*' && !key_is_escape(mkey) && mkey != '\\'
974 && mkey != ',')
975 {
976 mkey = ' ';
977 }
978 return mkey;
979 }
980
981 //////////////////////////////////////////////////////////////////////////////
982
in_inventory(const item_def & i)983 bool in_inventory(const item_def &i)
984 {
985 return i.pos == ITEM_IN_INVENTORY;
986 }
987
item_class_name(int type,bool terse)988 const char *item_class_name(int type, bool terse)
989 {
990 if (terse)
991 {
992 switch (type)
993 {
994 case OBJ_STAVES: return "magical staff";
995 case OBJ_MISCELLANY: return "misc";
996 default: return base_type_string((object_class_type) type);
997 }
998 }
999 else
1000 {
1001 switch (type)
1002 {
1003 case OBJ_GOLD: return "Gold";
1004 case OBJ_WEAPONS: return "Hand Weapons";
1005 case OBJ_MISSILES: return "Missiles";
1006 case OBJ_ARMOUR: return "Armour";
1007 case OBJ_WANDS: return "Wands";
1008 #if TAG_MAJOR_VERSION == 34
1009 case OBJ_FOOD: return "Comestibles";
1010 #endif
1011 case OBJ_SCROLLS: return "Scrolls";
1012 case OBJ_JEWELLERY: return "Jewellery";
1013 case OBJ_POTIONS: return "Potions";
1014 case OBJ_BOOKS: return "Books";
1015 case OBJ_STAVES: return "Magical Staves";
1016 #if TAG_MAJOR_VERSION == 34
1017 case OBJ_RODS: return "Rods";
1018 #endif
1019 case OBJ_ORBS: return "Orbs of Power";
1020 case OBJ_MISCELLANY: return "Miscellaneous";
1021 case OBJ_CORPSES: return "Carrion";
1022 case OBJ_RUNES: return "Runes of Zot";
1023 }
1024 }
1025 return "";
1026 }
1027
item_slot_name(equipment_type type)1028 const char* item_slot_name(equipment_type type)
1029 {
1030 switch (type)
1031 {
1032 case EQ_CLOAK: return "cloak";
1033 case EQ_HELMET: return "helmet";
1034 case EQ_GLOVES: return "gloves";
1035 case EQ_BOOTS: return "boots";
1036 case EQ_SHIELD: return "shield";
1037 case EQ_BODY_ARMOUR: return "body";
1038 default: return "";
1039 }
1040 }
1041
select_items(const vector<const item_def * > & items,const char * title,bool noselect,menu_type mtype)1042 vector<SelItem> select_items(const vector<const item_def*> &items,
1043 const char *title, bool noselect,
1044 menu_type mtype)
1045 {
1046 vector<SelItem> selected;
1047 if (!items.empty())
1048 {
1049 InvMenu menu;
1050 menu.set_type(mtype);
1051 menu.set_title(title);
1052 if (mtype == menu_type::pickup)
1053 menu.set_tag("pickup");
1054
1055 menu.load_items(items);
1056 int new_flags = noselect ? MF_NOSELECT
1057 : MF_MULTISELECT | MF_ALLOW_FILTER;
1058
1059 if (mtype == menu_type::sel_one)
1060 {
1061 new_flags |= MF_SINGLESELECT;
1062 new_flags &= ~MF_MULTISELECT;
1063 }
1064
1065 new_flags |= MF_ALLOW_FORMATTING;
1066 new_flags |= menu.get_flags() & MF_USE_TWO_COLUMNS;
1067 menu.set_flags(new_flags);
1068 menu.show();
1069 selected = menu.get_selitems();
1070 }
1071 return selected;
1072 }
1073
item_is_selected(const item_def & i,int selector)1074 bool item_is_selected(const item_def &i, int selector)
1075 {
1076 const object_class_type itype = i.base_type;
1077 if (selector == OSEL_ANY || selector == itype
1078 && itype != OBJ_ARMOUR)
1079 {
1080 return true;
1081 }
1082
1083 switch (selector)
1084 {
1085 case OBJ_ARMOUR:
1086 return itype == OBJ_ARMOUR && can_wear_armour(i, false, false);
1087
1088 case OSEL_WORN_ARMOUR:
1089 return itype == OBJ_ARMOUR && item_is_equipped(i);
1090
1091 case OSEL_UNIDENT:
1092 return !fully_identified(i) && itype != OBJ_BOOKS;
1093
1094 case OBJ_MISSILES:
1095 return itype == OBJ_MISSILES || itype == OBJ_WEAPONS;
1096
1097 case OSEL_THROWABLE:
1098 {
1099 if (itype != OBJ_WEAPONS && itype != OBJ_MISSILES)
1100 return false;
1101
1102 const launch_retval projected = is_launched(&you, you.weapon(), i);
1103
1104 if (projected == launch_retval::FUMBLED)
1105 return false;
1106
1107 return true;
1108 }
1109 case OBJ_WEAPONS:
1110 case OSEL_WIELD:
1111 return item_is_wieldable(i);
1112
1113 case OSEL_EVOKABLE:
1114 // assumes valid link...would break with evoking from floor?
1115 return item_is_evokable(i, true) && item_is_evokable(i, true, false);//evoke_check(i.link, true);
1116
1117 case OSEL_ENCHANTABLE_ARMOUR:
1118 return is_enchantable_armour(i, true);
1119
1120 case OSEL_CURSED_WORN:
1121 return i.cursed() && item_is_equipped(i);
1122
1123 #if TAG_MAJOR_VERSION == 34
1124 case OSEL_UNCURSED_WORN_ARMOUR:
1125 return !i.cursed() && item_is_equipped(i) && itype == OBJ_ARMOUR;
1126
1127 case OSEL_UNCURSED_WORN_JEWELLERY:
1128 return !i.cursed() && item_is_equipped(i) && itype == OBJ_JEWELLERY;
1129 #endif
1130
1131 case OSEL_BRANDABLE_WEAPON:
1132 return is_brandable_weapon(i, true);
1133
1134 case OSEL_ENCHANTABLE_WEAPON:
1135 return itype == OBJ_WEAPONS
1136 && !is_artefact(i)
1137 && (!item_ident(i, ISFLAG_KNOW_PLUSES)
1138 || i.plus < MAX_WPN_ENCHANT);
1139
1140 case OSEL_BLESSABLE_WEAPON:
1141 return is_brandable_weapon(i, you_worship(GOD_SHINING_ONE), true);
1142
1143 case OSEL_BEOGH_GIFT:
1144 return (itype == OBJ_WEAPONS
1145 || is_shield(i)
1146 || itype == OBJ_ARMOUR
1147 && get_armour_slot(i) == EQ_BODY_ARMOUR)
1148 && !item_is_equipped(i);
1149
1150 case OSEL_CURSABLE:
1151 return item_is_equipped(i) && item_is_cursable(i);
1152
1153 case OSEL_UNCURSED_WORN_RINGS:
1154 return !i.cursed() && item_is_equipped(i) && itype == OBJ_JEWELLERY
1155 && !jewellery_is_amulet(i);
1156
1157 case OSEL_QUIVER_ACTION:
1158 return in_inventory(i) && quiver::slot_to_action(i.link)->is_valid();
1159 case OSEL_QUIVER_ACTION_FORCE:
1160 return in_inventory(i) && quiver::slot_to_action(i.link, true)->is_valid();
1161
1162 default:
1163 return false;
1164 }
1165 }
1166
_get_inv_items_to_show(vector<const item_def * > & v,int selector,int excluded_slot)1167 static void _get_inv_items_to_show(vector<const item_def*> &v,
1168 int selector, int excluded_slot)
1169 {
1170 for (const auto &item : you.inv)
1171 {
1172 if (item.defined()
1173 && item.link != excluded_slot
1174 && item_is_selected(item, selector))
1175 {
1176 v.push_back(&item);
1177 }
1178 }
1179 }
1180
1181
1182 /**
1183 * Does the player have any items of the given type?
1184 *
1185 * @param selector A object_selector.
1186 * @param excluded_slot An item slot to ignore.
1187 * @param inspect_floor If true, also check the floor where the player is
1188 * standing.
1189 *
1190 * @return Whether there are any items matching the given
1191 * selector in the player's inventory.
1192 */
any_items_of_type(int selector,int excluded_slot,bool inspect_floor)1193 bool any_items_of_type(int selector, int excluded_slot, bool inspect_floor)
1194 {
1195 bool ret = any_of(begin(you.inv), end(you.inv),
1196 [=] (const item_def &item) -> bool
1197 {
1198 return item.defined() && item.link != excluded_slot
1199 && item_is_selected(item, selector);
1200 });
1201 if (!ret && inspect_floor)
1202 {
1203 auto item_floor = item_list_on_square(you.visible_igrd(you.pos()));
1204 ret = any_of(begin(item_floor), end(item_floor),
1205 [=] (const item_def* item) -> bool
1206 {
1207 return item->defined()
1208 && item_is_selected(*item, selector);
1209 });
1210 }
1211 return ret;
1212 }
1213
1214 // Use title = nullptr for stock Inventory title
1215 // type = menu_type::drop allows the multidrop toggle
_invent_select(const char * title=nullptr,menu_type type=menu_type::invlist,int item_selector=OSEL_ANY,int excluded_slot=-1,int flags=MF_NOSELECT,invtitle_annotator titlefn=nullptr,vector<SelItem> * items=nullptr,vector<text_pattern> * filter=nullptr,Menu::selitem_tfn selitemfn=nullptr,const vector<SelItem> * pre_select=nullptr)1216 static unsigned char _invent_select(const char *title = nullptr,
1217 menu_type type = menu_type::invlist,
1218 int item_selector = OSEL_ANY,
1219 int excluded_slot = -1,
1220 int flags = MF_NOSELECT,
1221 invtitle_annotator titlefn = nullptr,
1222 vector<SelItem> *items = nullptr,
1223 vector<text_pattern> *filter = nullptr,
1224 Menu::selitem_tfn selitemfn = nullptr,
1225 const vector<SelItem> *pre_select = nullptr)
1226 {
1227 InvMenu menu(flags | MF_ALLOW_FORMATTING);
1228
1229 menu.set_preselect(pre_select);
1230 menu.set_title_annotator(titlefn);
1231 menu.f_selitem = selitemfn;
1232 if (filter)
1233 menu.set_select_filter(*filter);
1234 menu.load_inv_items(item_selector, excluded_slot);
1235 menu.set_type(type);
1236
1237 // Don't override title if there are no items.
1238 if (title && menu.item_count())
1239 menu.set_title(title);
1240
1241 menu.show(true);
1242
1243 if (items)
1244 *items = menu.get_selitems();
1245
1246 return menu.getkey();
1247 }
1248
display_inventory()1249 void display_inventory()
1250 {
1251 InvMenu menu(MF_SINGLESELECT | MF_ALLOW_FORMATTING);
1252 menu.load_inv_items(OSEL_ANY, -1);
1253 menu.set_type(menu_type::invlist);
1254
1255 menu.on_single_selection = [](const MenuEntry& item)
1256 {
1257 unsigned char select = item.hotkeys[0];
1258 const int invidx = letter_to_index(select);
1259 ASSERT(you.inv[invidx].defined());
1260 return describe_item(you.inv[invidx]);
1261 };
1262
1263 menu.show(true);
1264 if (!crawl_state.doing_prev_cmd_again)
1265 {
1266 redraw_screen();
1267 update_screen();
1268 }
1269 }
1270
1271 // Reads in digits for a count and apprends then to val, the
1272 // return value is the character that stopped the reading.
_get_invent_quant(unsigned char keyin,int & quant)1273 static unsigned char _get_invent_quant(unsigned char keyin, int &quant)
1274 {
1275 quant = keyin - '0';
1276
1277 while (true)
1278 {
1279 keyin = get_ch();
1280
1281 if (!isadigit(keyin))
1282 break;
1283
1284 quant *= 10;
1285 quant += (keyin - '0');
1286
1287 if (quant > 9999999)
1288 {
1289 quant = 9999999;
1290 keyin = '\0';
1291 break;
1292 }
1293 }
1294
1295 return keyin;
1296 }
1297
_drop_selitem_text(const vector<MenuEntry * > * s)1298 static string _drop_selitem_text(const vector<MenuEntry*> *s)
1299 {
1300 bool extraturns = false;
1301
1302 if (s->empty())
1303 return "";
1304
1305 for (MenuEntry *entry : *s)
1306 {
1307 const item_def *item = static_cast<item_def *>(entry->data);
1308 const int eq = get_equip_slot(item);
1309 if (eq > EQ_WEAPON && eq < NUM_EQUIP)
1310 {
1311 extraturns = true;
1312 break;
1313 }
1314 }
1315
1316 return make_stringf(" (%u%s turn%s)",
1317 (unsigned int)s->size(),
1318 extraturns? "+" : "",
1319 s->size() > 1? "s" : "");
1320 }
1321
_drop_prompt(bool as_menu_title,bool menu_autopickup_mode)1322 static string _drop_prompt(bool as_menu_title, bool menu_autopickup_mode)
1323 {
1324 string prompt_base;
1325
1326 if (as_menu_title && menu_autopickup_mode)
1327 prompt_base = "Drop (and turn off autopickup for) what? ";
1328 else if (as_menu_title)
1329 prompt_base = "Drop what? ";
1330 else
1331 prompt_base = "Drop what? ";
1332 return prompt_base + slot_description()
1333 #ifdef TOUCH_UI
1334 + " (<Enter> or tap header to drop)";
1335 #else
1336 + " (_ for help)";
1337 #endif
1338 }
1339
_drop_menu_titlefn(const Menu * m,const string &)1340 static string _drop_menu_titlefn(const Menu *m, const string &)
1341 {
1342 const InvMenu *invmenu = static_cast<const InvMenu *>(m);
1343 return _drop_prompt(true, invmenu->mode_special_drop());
1344 }
1345
1346 /**
1347 * Prompt the player to select zero or more items to drop.
1348 * TODO: deduplicate/merge with prompt_invent_item().
1349 *
1350 * @param Items already selected to drop.
1351 * @return The total set of items the player's chosen to drop.
1352 */
prompt_drop_items(const vector<SelItem> & preselected_items)1353 vector<SelItem> prompt_drop_items(const vector<SelItem> &preselected_items)
1354 {
1355 unsigned char keyin = '?';
1356 int ret = PROMPT_ABORT;
1357
1358 bool need_redraw = false;
1359 bool need_prompt = true;
1360 bool need_getch = false;
1361
1362 vector<SelItem> items;
1363 int count = -1;
1364 while (true)
1365 {
1366 if (need_redraw && !crawl_state.doing_prev_cmd_again)
1367 {
1368 redraw_screen();
1369 update_screen();
1370 clear_messages();
1371 }
1372
1373 if (need_prompt)
1374 {
1375 const string prompt = _drop_prompt(false, false);
1376 mprf(MSGCH_PROMPT, "%s (<w>?</w> for menu, <w>Esc</w> to quit)",
1377 prompt.c_str());
1378 }
1379
1380 if (need_getch)
1381 keyin = get_ch();
1382
1383 need_redraw = false;
1384 need_prompt = true;
1385 need_getch = true;
1386
1387 if (keyin == '_')
1388 show_specific_help("pick-up");
1389 else if (keyin == '?' || keyin == '*' || keyin == ',')
1390 {
1391 // The "view inventory listing" mode.
1392 const int ch = _invent_select("",
1393 menu_type::drop,
1394 OSEL_ANY,
1395 -1,
1396 MF_MULTISELECT | MF_ALLOW_FILTER,
1397 _drop_menu_titlefn,
1398 &items,
1399 &Options.drop_filter,
1400 _drop_selitem_text,
1401 &preselected_items);
1402
1403 if (key_is_escape(ch))
1404 {
1405 keyin = ch;
1406 need_prompt = false;
1407 need_getch = false;
1408 }
1409 else
1410 {
1411 keyin = 0;
1412 need_prompt = true;
1413 need_getch = true;
1414 }
1415
1416 if (!items.empty())
1417 {
1418 if (!crawl_state.doing_prev_cmd_again)
1419 {
1420 redraw_screen();
1421 update_screen();
1422 clear_messages();
1423 }
1424
1425 for (SelItem &sel : items)
1426 sel.slot = letter_to_index(sel.slot);
1427 return items;
1428 }
1429
1430 need_redraw = !(keyin == '?' || keyin == '*'
1431 || keyin == ',' || keyin == '+');
1432 }
1433 else if (isadigit(keyin))
1434 {
1435 // The "read in quantity" mode
1436 keyin = _get_invent_quant(keyin, count);
1437
1438 need_prompt = false;
1439 need_getch = false;
1440 }
1441 else if (key_is_escape(keyin)
1442 || (Options.easy_quit_item_prompts && keyin == ' '))
1443 {
1444 ret = PROMPT_ABORT;
1445 break;
1446 }
1447 else if (isaalpha(keyin))
1448 {
1449 ret = letter_to_index(keyin);
1450
1451 if (!you.inv[ret].defined())
1452 mpr("You don't have any such object.");
1453 else
1454 break;
1455 }
1456 else if (keyin == ';')
1457 {
1458 ret = you.last_unequip;
1459 break;
1460 }
1461 else if (!isspace(keyin))
1462 {
1463 // We've got a character we don't understand...
1464 canned_msg(MSG_HUH);
1465 }
1466 else
1467 {
1468 // We're going to loop back up, so don't draw another prompt.
1469 need_prompt = false;
1470 }
1471 }
1472
1473 if (ret != PROMPT_ABORT)
1474 items.emplace_back(ret, count, &you.inv[ret]);
1475 return items;
1476 }
1477
item_matches_digit_inscription(item_def & item,char digit,operation_types oper)1478 static bool item_matches_digit_inscription(item_def &item, char digit, operation_types oper)
1479 {
1480 const string& r(item.inscription);
1481 const char iletter = static_cast<char>(oper);
1482 for (unsigned int j = 0; j + 2 < r.size(); ++j)
1483 if (r[j] == '@' && (r[j+1] == iletter || r[j+1] == '*') && r[j+2] == digit)
1484 return true;
1485 return false;
1486 }
1487
digit_inscription_to_item(char digit,operation_types oper)1488 item_def *digit_inscription_to_item(char digit, operation_types oper)
1489 {
1490 for (int i = 0; i < ENDOFPACK; ++i)
1491 if (you.inv[i].defined()
1492 && item_matches_digit_inscription(you.inv[i], digit, oper))
1493 {
1494 return &you.inv[i];
1495 }
1496
1497 for (stack_iterator si(you.pos(), true); si; ++si)
1498 if (item_matches_digit_inscription(*si, digit, oper))
1499 return &*si;
1500
1501 return nullptr;
1502 }
1503
_has_warning_inscription(const item_def & item,operation_types oper)1504 static bool _has_warning_inscription(const item_def& item,
1505 operation_types oper)
1506 {
1507 const char iletter = static_cast<char>(oper);
1508
1509 const string& r(item.inscription);
1510 for (unsigned int i = 0; i + 1 < r.size(); ++i)
1511 {
1512 if (r[i] == '!')
1513 {
1514 if (r[i+1] == iletter || r[i+1] == '*')
1515 return true;
1516 else if (oper == OPER_ZAP && r[i+1] == 'z') // for the 0.3.4. keys
1517 return true;
1518 else if (oper == OPER_EVOKE
1519 && (r[i+1] == 'V' || toalower(r[i+1]) == 'z'))
1520 {
1521 return true;
1522 }
1523 }
1524 }
1525
1526 return false;
1527 }
1528
1529 // In order to equip this item, we may need to remove an old item in the
1530 // corresponding slot which has a warning inscription. If this is the case,
1531 // prompt the user for confirmation.
check_old_item_warning(const item_def & item,operation_types oper,bool check_melded)1532 bool check_old_item_warning(const item_def& item,
1533 operation_types oper,
1534 bool check_melded)
1535 {
1536 item_def old_item;
1537 string prompt;
1538 bool penance = false;
1539 if (oper == OPER_WIELD) // can we safely unwield old item?
1540 {
1541 if (!you.slot_item(EQ_WEAPON, check_melded))
1542 return true;
1543
1544 int equip = you.equip[EQ_WEAPON];
1545 if (equip == -1 || item.link == equip)
1546 return true;
1547
1548 old_item = *you.slot_item(EQ_WEAPON, check_melded);
1549 if (!needs_handle_warning(old_item, OPER_WIELD, penance))
1550 return true;
1551
1552 prompt += "Really unwield ";
1553 }
1554 else if (oper == OPER_WEAR) // can we safely take off old item?
1555 {
1556 if (item.base_type != OBJ_ARMOUR)
1557 return true;
1558
1559 equipment_type eq_slot = get_armour_slot(item);
1560 int equip = you.equip[eq_slot];
1561 if (equip == -1 || item.link == equip)
1562 return true;
1563
1564 old_item = you.inv[you.equip[eq_slot]];
1565
1566 if (!needs_handle_warning(old_item, OPER_TAKEOFF, penance))
1567 return true;
1568
1569 prompt += "Really take off ";
1570 }
1571 else if (oper == OPER_PUTON) // can we safely remove old item?
1572 {
1573 if (item.base_type != OBJ_JEWELLERY)
1574 return true;
1575
1576 if (jewellery_is_amulet(item))
1577 {
1578 int equip = you.equip[EQ_AMULET];
1579 if (equip == -1 || item.link == equip)
1580 return true;
1581
1582 old_item = you.inv[equip];
1583 if (!needs_handle_warning(old_item, OPER_TAKEOFF, penance))
1584 return true;
1585
1586 prompt += "Really remove ";
1587 }
1588 else // rings handled in prompt_ring_to_remove
1589 return true;
1590 }
1591 else // anything else doesn't have a counterpart
1592 return true;
1593
1594 // now ask
1595 if (old_item.cursed())
1596 prompt += "and destroy ";
1597 prompt += old_item.name(DESC_INVENTORY);
1598 prompt += "?";
1599 if (penance)
1600 prompt += " This could place you under penance!";
1601 return yesno(prompt.c_str(), false, 'n');
1602 }
1603
_operation_verb(operation_types oper)1604 static string _operation_verb(operation_types oper)
1605 {
1606 switch (oper)
1607 {
1608 case OPER_WIELD: return "wield";
1609 case OPER_QUAFF: return "quaff";
1610 case OPER_DROP: return "drop";
1611 case OPER_TAKEOFF: return "take off";
1612 case OPER_WEAR: return "wear";
1613 case OPER_PUTON: return "put on";
1614 case OPER_REMOVE: return "remove";
1615 case OPER_READ: return "read";
1616 case OPER_MEMORISE: return "memorise from";
1617 case OPER_ZAP: return "zap";
1618 case OPER_FIRE: return "fire";
1619 case OPER_EVOKE: return "evoke";
1620 case OPER_DESTROY: return "destroy";
1621 case OPER_QUIVER: return "quiver";
1622 case OPER_ANY:
1623 default:
1624 return "choose";
1625 }
1626 }
1627
_is_wielded(const item_def & item)1628 static bool _is_wielded(const item_def &item)
1629 {
1630 int equip = you.equip[EQ_WEAPON];
1631 return equip != -1 && item.link == equip;
1632 }
1633
_is_known_no_tele_item(const item_def & item)1634 static bool _is_known_no_tele_item(const item_def &item)
1635 {
1636 if (!is_artefact(item))
1637 return false;
1638
1639 return artefact_known_property(item, ARTP_PREVENT_TELEPORTATION);
1640 }
1641
needs_notele_warning(const item_def & item,operation_types oper)1642 bool needs_notele_warning(const item_def &item, operation_types oper)
1643 {
1644 return (oper == OPER_PUTON || oper == OPER_WEAR
1645 || oper == OPER_WIELD && !_is_wielded(item))
1646 && (_is_known_no_tele_item(item) && you.duration[DUR_TELEPORT]);
1647 }
1648
needs_handle_warning(const item_def & item,operation_types oper,bool & penance)1649 bool needs_handle_warning(const item_def &item, operation_types oper,
1650 bool &penance)
1651 {
1652 if (_has_warning_inscription(item, oper))
1653 return true;
1654
1655 // Curses first. Warn if something would take off (i.e. destroy) the cursed item.
1656 if (item.cursed()
1657 && (oper == OPER_WIELD && is_weapon(item)
1658 || oper == OPER_PUTON || oper == OPER_WEAR
1659 || oper == OPER_TAKEOFF || oper == OPER_REMOVE))
1660 {
1661 return true;
1662 }
1663
1664 // The consequences of evokables are generally known.
1665 if (item.base_type == OBJ_MISCELLANY
1666 && oper == OPER_EVOKE && god_hates_item(item))
1667 {
1668 penance = true;
1669 return true;
1670 }
1671
1672 // Everything else depends on knowing the item subtype/brand.
1673 if (!item_type_known(item))
1674 return false;
1675
1676 if (oper == OPER_REMOVE
1677 && item.is_type(OBJ_JEWELLERY, AMU_FAITH)
1678 && !(you_worship(GOD_RU) && you.piety >= piety_breakpoint(5))
1679 && !you_worship(GOD_GOZAG)
1680 && !you_worship(GOD_NO_GOD)
1681 && !you_worship(GOD_XOM)
1682 && !you_worship(GOD_ASHENZARI))
1683 {
1684 return true;
1685 }
1686
1687 if (needs_notele_warning(item, oper))
1688 return true;
1689
1690 if (oper == OPER_ATTACK && god_hates_item(item)
1691 #if TAG_MAJOR_VERSION == 34
1692 && !you_worship(GOD_PAKELLAS)
1693 #endif
1694 )
1695 {
1696 penance = true;
1697 return true;
1698 }
1699
1700 if (oper == OPER_WIELD // unwielding uses OPER_WIELD too
1701 && is_weapon(item))
1702 {
1703 if (get_weapon_brand(item) == SPWPN_DISTORTION
1704 && !have_passive(passive_t::safe_distortion))
1705 {
1706 return true;
1707 }
1708
1709 if (is_artefact(item) && artefact_property(item, ARTP_CONTAM))
1710 {
1711 if (_is_wielded(item) && you_worship(GOD_ZIN))
1712 penance = true;
1713 return true;
1714 }
1715
1716 if (is_artefact(item) && (artefact_property(item, ARTP_DRAIN)
1717 || artefact_property(item, ARTP_FRAGILE)))
1718 {
1719 return true;
1720 }
1721 }
1722
1723 if (oper == OPER_PUTON || oper == OPER_WEAR || oper == OPER_TAKEOFF
1724 || oper == OPER_REMOVE)
1725 {
1726 if (is_artefact(item) && artefact_property(item, ARTP_CONTAM))
1727 {
1728 if ((oper == OPER_TAKEOFF || oper == OPER_REMOVE)
1729 && you_worship(GOD_ZIN))
1730 {
1731 penance = true;
1732 }
1733 return true;
1734 }
1735
1736 if (is_artefact(item) && (artefact_property(item, ARTP_DRAIN)
1737 || artefact_property(item, ARTP_FRAGILE)))
1738 {
1739 return true;
1740 }
1741 }
1742
1743 if (oper == OPER_EVOKE && god_hates_item(item))
1744 {
1745 penance = true;
1746 return true;
1747 }
1748
1749 return false;
1750 }
1751
1752 // If there are warning inscriptions associated with performing this operation
1753 // on this item, prompt the user for confirmation. Return true if all prompts
1754 // are OK'd.
check_warning_inscriptions(const item_def & item,operation_types oper)1755 bool check_warning_inscriptions(const item_def& item,
1756 operation_types oper)
1757 {
1758 bool penance = false;
1759 if (item.defined()
1760 && needs_handle_warning(item, oper, penance))
1761 {
1762 // When it's about destroying an item, don't even ask.
1763 // If the player really wants to do that, they'll have
1764 // to remove the inscription.
1765 if (oper == OPER_DESTROY)
1766 return false;
1767
1768 // Common pattern for wield/wear/put:
1769 // - if the player isn't capable of equipping it, return true
1770 // immediately. No point warning, since the op is impossible.
1771 // - if the item is already worn, treat this as the corresponding
1772 // unequip operation
1773 if (oper == OPER_WIELD)
1774 {
1775 // Can't use can_wield in item-use.cc because it wants
1776 // a non-const item_def.
1777 if (!you.can_wield(item))
1778 return true;
1779
1780 int equip = you.equip[EQ_WEAPON];
1781 if (equip != -1 && item.link == equip)
1782 return check_old_item_warning(item, oper);
1783 }
1784 else if (oper == OPER_WEAR)
1785 {
1786 if (!can_wear_armour(item, false, false))
1787 return true;
1788
1789 int equip = you.equip[get_armour_slot(item)];
1790 if (equip != -1 && item.link == equip)
1791 return check_old_item_warning(item, oper);
1792 }
1793 else if (oper == OPER_PUTON)
1794 {
1795 if (item.base_type != OBJ_JEWELLERY)
1796 return true;
1797
1798 if (jewellery_is_amulet(item))
1799 {
1800 int equip = you.equip[EQ_AMULET];
1801 if (equip != -1 && item.link == equip)
1802 return check_old_item_warning(item, oper);
1803 }
1804 else
1805 {
1806 for (int slots = EQ_FIRST_JEWELLERY; slots <= EQ_LAST_JEWELLERY; ++slots)
1807 {
1808 if (slots == EQ_AMULET)
1809 continue;
1810
1811 int equip = you.equip[slots];
1812 if (equip != -1 && item.link == equip)
1813 return check_old_item_warning(item, oper);
1814 }
1815 }
1816 }
1817 else if (oper == OPER_REMOVE || oper == OPER_TAKEOFF)
1818 {
1819 // Don't ask if it will fail anyway.
1820 if (item.cursed())
1821 return true;
1822 }
1823
1824 // XXX: duplicates a check in delay.cc:_finish_delay()
1825 string prompt = "Really " + _operation_verb(oper) + " ";
1826 prompt += (in_inventory(item) ? item.name(DESC_INVENTORY)
1827 : item.name(DESC_A));
1828 if (needs_notele_warning(item, oper)
1829 && item_ident(item, ISFLAG_KNOW_TYPE))
1830 {
1831 prompt += " while about to teleport";
1832 }
1833 prompt += "?";
1834 if (penance)
1835 prompt += " This could place you under penance!";
1836 return yesno(prompt.c_str(), false, 'n')
1837 && check_old_item_warning(item, oper);
1838 }
1839 else
1840 return check_old_item_warning(item, oper);
1841 }
1842
1843 /**
1844 * Prompts the user for an item.
1845 *
1846 * Prompts the user for an item, handling the '?' and '*' listings,
1847 * and returns the inventory slot to the caller (which if
1848 * must_exist is true (the default) will be an assigned item), with
1849 * a positive quantity.
1850 *
1851 * Note: Does not check if the item is appropriate.
1852 *
1853 * @param prompt The question to ask the user.
1854 * @param mtype The menu type.
1855 * @param type_expect The object_class_type or object_selector for
1856 * items to be listed.
1857 * @param oper The operation_type that will be used on the result.
1858 * Modifies some checks, including applicability of
1859 * warning inscriptions.
1860 * @param flags See comments on invent_prompt_flags.
1861 * @param other_valid_char A character that, if not '\0', will cause
1862 * PROMPT_GOT_SPECIAL to be returned when pressed.
1863 *
1864 * @return the inventory slot of an item or one of the following special values
1865 * - PROMPT_ABORT: if the player hits escape.
1866 * - PROMPT_GOT_SPECIAL: if the player hits the "other_valid_char".
1867 * - PROMPT_NOTHING: if there are no matching items.
1868 */
prompt_invent_item(const char * prompt,menu_type mtype,int type_expect,operation_types oper,invent_prompt_flags flags,const char other_valid_char)1869 int prompt_invent_item(const char *prompt,
1870 menu_type mtype, int type_expect,
1871 operation_types oper,
1872 invent_prompt_flags flags,
1873 const char other_valid_char)
1874 {
1875 const bool do_warning = !(flags & invprompt_flag::no_warning);
1876 const bool allow_list_known = !(flags & invprompt_flag::hide_known);
1877 const bool must_exist = !(flags & invprompt_flag::unthings_ok);
1878 const bool auto_list = !(flags & invprompt_flag::manual_list);
1879 const bool allow_easy_quit = !(flags & invprompt_flag::escape_only);
1880
1881 if (!any_items_of_type(type_expect) && type_expect != OSEL_WIELD
1882 && type_expect != OSEL_QUIVER_ACTION)
1883 {
1884 mprf(MSGCH_PROMPT, "%s",
1885 no_selectables_message(type_expect).c_str());
1886 return PROMPT_NOTHING;
1887 }
1888
1889 unsigned char keyin = 0;
1890 int ret = PROMPT_ABORT;
1891
1892 int current_type_expected = type_expect;
1893 bool need_redraw = false;
1894 bool need_prompt = true;
1895 bool need_getch = true;
1896
1897 if (auto_list)
1898 {
1899 need_prompt = false;
1900 need_getch = false;
1901
1902 if (any_items_of_type(type_expect))
1903 keyin = '?';
1904 else
1905 keyin = '*';
1906 }
1907
1908 while (true)
1909 {
1910 if (need_redraw && !crawl_state.doing_prev_cmd_again)
1911 {
1912 redraw_screen();
1913 clear_messages();
1914 }
1915
1916 if (need_prompt)
1917 {
1918 mprf(MSGCH_PROMPT, "%s (<w>?</w> for menu, <w>Esc</w> to quit)",
1919 prompt);
1920 }
1921 else
1922 flush_prev_message();
1923
1924 if (need_getch)
1925 keyin = get_ch();
1926
1927 need_redraw = false;
1928 need_prompt = true;
1929 need_getch = true;
1930
1931 // Note: We handle any "special" character first, so that
1932 // it can be used to override the others.
1933 if (other_valid_char != 0 && keyin == other_valid_char)
1934 {
1935 ret = PROMPT_GOT_SPECIAL;
1936 break;
1937 }
1938
1939 // TODO: it seems like for some uses of this function, `*` shouldn't
1940 // be allowed at all, e.g. evoke.
1941 if (keyin == '?' || keyin == '*')
1942 {
1943 // The "view inventory listing" mode.
1944 vector< SelItem > items;
1945 const auto last_keyin = keyin;
1946 current_type_expected = keyin == '*' ? OSEL_ANY : type_expect;
1947 int mflags = MF_SINGLESELECT | MF_ANYPRINTABLE | MF_NO_SELECT_QTY;
1948 if (other_valid_char == '-')
1949 mflags |= MF_SPECIAL_MINUS;
1950
1951 while (true)
1952 {
1953 keyin = _invent_select(prompt, mtype, current_type_expected, -1,
1954 mflags, nullptr, &items);
1955
1956 if (allow_list_known && keyin == '\\')
1957 {
1958 check_item_knowledge();
1959 continue;
1960 }
1961 break;
1962 }
1963
1964 // a continue at this point has need_prompt = need_getch = true
1965
1966 // let `?` toggle the menu altogether. If this is a non-autolist,
1967 // return to the prompt, otherwise we will pass escape to the
1968 // abort handling below. (TODO: is this the right behavior?)
1969 // TODO: some versions of _invent_select, such as wield, never
1970 // return '?'. Is this a problem?
1971 if (keyin == '?' || key_is_escape(keyin) && !auto_list)
1972 continue;
1973
1974 if (keyin == '*')
1975 {
1976 // let `*` act as a toggle. This is a slightly wacky
1977 // implementation in that '?' as a toggle does something
1978 // entirely different...
1979 need_prompt = need_getch = false;
1980 if (last_keyin == '*')
1981 keyin = '?';
1982 else
1983 keyin = '*';
1984 continue;
1985 }
1986
1987 if (other_valid_char != 0 && keyin == other_valid_char)
1988 {
1989 // need to handle overrides...ugly code duplication
1990 ret = PROMPT_GOT_SPECIAL;
1991 break;
1992 }
1993 }
1994
1995 if (isadigit(keyin))
1996 {
1997 // scan for our item
1998 item_def *item = digit_inscription_to_item(keyin, oper);
1999 if (item && in_inventory(*item))
2000 {
2001 ret = item->link;
2002 if (!do_warning || check_warning_inscriptions(*item, oper))
2003 break;
2004 }
2005 }
2006 else if (key_is_escape(keyin)
2007 || (Options.easy_quit_item_prompts
2008 && allow_easy_quit && keyin == ' '))
2009 {
2010 ret = PROMPT_ABORT;
2011 break;
2012 }
2013 else if (allow_list_known && keyin == '\\')
2014 {
2015 check_item_knowledge();
2016 keyin = '?';
2017 need_getch = false;
2018 }
2019 else if (isaalpha(keyin))
2020 {
2021 ret = letter_to_index(keyin);
2022
2023 if (must_exist && !you.inv[ret].defined())
2024 mpr("You don't have any such object.");
2025 else if (must_exist && !item_is_selected(you.inv[ret],
2026 current_type_expected))
2027 {
2028 mpr("That's the wrong kind of item! (Use * to select it.)");
2029 }
2030 else if (!do_warning || check_warning_inscriptions(you.inv[ret], oper))
2031 break;
2032 }
2033 else if (keyin == ';')
2034 {
2035 ret = you.last_unequip;
2036 break;
2037 }
2038 else if (!isspace(keyin))
2039 {
2040 // We've got a character we don't understand...
2041 canned_msg(MSG_HUH);
2042 }
2043 else
2044 {
2045 // We're going to loop back up, so don't draw another prompt.
2046 need_prompt = false;
2047 }
2048 }
2049
2050 return ret;
2051 }
2052
prompt_failed(int retval)2053 bool prompt_failed(int retval)
2054 {
2055 if (retval != PROMPT_ABORT && retval != PROMPT_NOTHING)
2056 return false;
2057
2058 if (retval == PROMPT_ABORT)
2059 canned_msg(MSG_OK);
2060
2061 crawl_state.cancel_cmd_repeat();
2062
2063 return true;
2064 }
2065
2066 // Most items are wieldable, but this function check for items that needs to be
2067 // wielded to be used normally.
item_is_wieldable(const item_def & item)2068 bool item_is_wieldable(const item_def &item)
2069 {
2070 return is_weapon(item) && !you.has_mutation(MUT_NO_GRASPING);
2071 }
2072
2073 /// Does the item only serve to produce summons or allies?
_item_ally_only(const item_def & item)2074 static bool _item_ally_only(const item_def &item)
2075 {
2076 if (item.base_type == OBJ_WANDS)
2077 return item.sub_type == WAND_CHARMING;
2078 else if (item.base_type == OBJ_MISCELLANY)
2079 {
2080 switch (item.sub_type)
2081 {
2082 case MISC_PHANTOM_MIRROR:
2083 case MISC_HORN_OF_GERYON:
2084 case MISC_BOX_OF_BEASTS:
2085 return true;
2086 default:
2087 return false;
2088 }
2089 }
2090 return false;
2091 }
2092
2093 /**
2094 * Return whether an item can be evoked.
2095 *
2096 * @param item The item to check
2097 * @param unskilled Do items that don't use Evocations skill (weapons of
2098 * reaching and tremorstones) count?
2099 * @param msg Whether we need to print a message.
2100 * @param equip When false, ignore wield and meld requirements.
2101 */
item_is_evokable(const item_def & item,bool unskilled,bool msg,bool equip)2102 bool item_is_evokable(const item_def &item, bool unskilled,
2103 bool msg, bool equip)
2104 {
2105 const string error = item_is_melded(item)
2106 ? "Your " + item.name(DESC_QUALNAME) + " is melded into your body."
2107 : "That item can only be evoked when wielded.";
2108
2109 const bool no_evocables = you.get_mutation_level(MUT_NO_ARTIFICE);
2110 const char* const no_evocable_error = "You cannot evoke magical items.";
2111
2112 if (is_unrandom_artefact(item))
2113 {
2114 const unrandart_entry* entry = get_unrand_entry(item.unrand_idx);
2115
2116 if ((entry->evoke_func || entry->targeted_evoke_func)
2117 && item_type_known(item))
2118 {
2119 if (no_evocables)
2120 {
2121 if (msg)
2122 mpr(no_evocable_error);
2123 return false;
2124 }
2125
2126 if (item_is_equipped(item) && !item_is_melded(item) || !equip)
2127 return true;
2128
2129 if (msg)
2130 mpr(error);
2131
2132 return false;
2133 }
2134 // Unrandart might still be evokable (e.g., reaching)
2135 }
2136
2137 if (no_evocables
2138 && item.base_type != OBJ_WEAPONS // reaching is ok.
2139 && !(item.base_type == OBJ_MISCELLANY
2140 && item.sub_type == MISC_ZIGGURAT)) // zigfigs are OK.
2141 {
2142 // the rest are forbidden under sac evocables.
2143 if (msg)
2144 mpr(no_evocable_error);
2145 return false;
2146 }
2147
2148 // TODO: check other summoning constraints here?
2149 if (_item_ally_only(item) && you.has_mutation(MUT_NO_LOVE))
2150 {
2151 if (msg)
2152 mpr("That item cannot be used by those hated by all!");
2153 return false;
2154 }
2155
2156 const bool wielded = !equip || you.equip[EQ_WEAPON] == item.link
2157 && !item_is_melded(item);
2158
2159 switch (item.base_type)
2160 {
2161 case OBJ_WANDS:
2162 return true;
2163
2164 // TODO: move these out of evoke
2165 case OBJ_WEAPONS:
2166 if ((!wielded || !unskilled) && !msg)
2167 return false;
2168
2169 // XX code duplication with evoke_check
2170 if (unskilled
2171 && (weapon_reach(item) > REACH_NONE && item_type_known(item)
2172 || you.weapon() && is_range_weapon(*you.weapon())))
2173 {
2174 if (!wielded)
2175 {
2176 if (msg)
2177 mpr(error);
2178 return false;
2179 }
2180 return true;
2181 }
2182
2183 if (msg)
2184 mpr("That item cannot be evoked!");
2185 return false;
2186
2187 #if TAG_MAJOR_VERSION == 34
2188 case OBJ_MISCELLANY:
2189 if (item.sub_type != MISC_BUGGY_LANTERN_OF_SHADOWS
2190 && item.sub_type != MISC_BUGGY_EBONY_CASKET)
2191 {
2192 return true;
2193 }
2194 // removed items fallthrough to failure
2195 #endif
2196
2197 default:
2198 if (msg)
2199 mpr("That item cannot be evoked!");
2200 return false;
2201 }
2202 }
2203
2204 /**
2205 * What xp-charged evocable items is the player currently devoting XP to, if
2206 * any?
2207 *
2208 * @param[out] evokers A vector, to be filled with a list of the elemental
2209 * evokers that the player is currently charging.
2210 * (Only one of a type is charged at a time.)
2211 */
list_charging_evokers(FixedVector<item_def *,NUM_MISCELLANY> & evokers)2212 void list_charging_evokers(FixedVector<item_def*, NUM_MISCELLANY> &evokers)
2213 {
2214 for (auto &item : you.inv)
2215 {
2216 // can't charge non-evokers, or evokers that are full
2217 if (!is_xp_evoker(item) || evoker_debt(item.sub_type) == 0)
2218 continue;
2219
2220 evokers[item.sub_type] = &item;
2221 }
2222 }
2223
identify_inventory()2224 void identify_inventory()
2225 {
2226 for (auto &item : you.inv)
2227 {
2228 if (item.defined())
2229 {
2230 set_ident_type(item, true);
2231 set_ident_flags(item, ISFLAG_IDENT_MASK);
2232 }
2233 }
2234 }
2235