diff options
Diffstat (limited to 'crawl-ref/source')
-rw-r--r-- | crawl-ref/source/Kills.cc | 50 | ||||
-rw-r--r-- | crawl-ref/source/Kills.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/acr.cc | 4 | ||||
-rw-r--r-- | crawl-ref/source/command.cc | 4 | ||||
-rw-r--r-- | crawl-ref/source/debug.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 14 | ||||
-rw-r--r-- | crawl-ref/source/externs.h | 44 | ||||
-rw-r--r-- | crawl-ref/source/food.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/initfile.cc | 99 | ||||
-rw-r--r-- | crawl-ref/source/invent.cc | 162 | ||||
-rw-r--r-- | crawl-ref/source/invent.h | 19 | ||||
-rw-r--r-- | crawl-ref/source/item_use.cc | 20 | ||||
-rw-r--r-- | crawl-ref/source/itemname.cc | 132 | ||||
-rw-r--r-- | crawl-ref/source/libutil.cc | 54 | ||||
-rw-r--r-- | crawl-ref/source/libutil.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/spells1.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/spells4.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/spl-book.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/stash.cc | 5 |
19 files changed, 461 insertions, 160 deletions
diff --git a/crawl-ref/source/Kills.cc b/crawl-ref/source/Kills.cc index b8da6d4b82..3652662dde 100644 --- a/crawl-ref/source/Kills.cc +++ b/crawl-ref/source/Kills.cc @@ -352,56 +352,6 @@ static const char *modifier_suffixes[] = "zombie", "skeleton", "simulacrum", NULL, }; -// Pluralises a monster name. This'll need to be updated for correctness -// whenever new monsters are added. -std::string pluralise(const std::string &name, - const char *no_of[]) -{ - std::string::size_type pos; - - // Pluralise first word of names like 'eye of draining', but only if the - // whole name is not suffixed by a modifier, such as 'zombie' or 'skeleton' - if ( (pos = name.find(" of ")) != std::string::npos - && !ends_with(name, no_of) ) - return pluralise(name.substr(0, pos)) + name.substr(pos); - else if (ends_with(name, "us")) - // Fungus, ufetubus, for instance. - return name.substr(0, name.length() - 2) + "i"; - else if (ends_with(name, "larva") || ends_with(name, "amoeba")) - // Giant amoebae sounds a little weird, to tell the truth. - return name + "e"; - else if (ends_with(name, "ex")) - // Vortex; vortexes is legal, but the classic plural is cooler. - return name.substr(0, name.length() - 2) + "ices"; - else if (ends_with(name, "cyclops")) - return name.substr(0, name.length() - 1) + "es"; - else if (ends_with(name, "y")) - return name.substr(0, name.length() - 1) + "ies"; - else if (ends_with(name, "elf") || ends_with(name, "olf")) - // Elf, wolf. Dwarfs can stay dwarfs, if there were dwarfs. - return name.substr(0, name.length() - 1) + "ves"; - else if (ends_with(name, "mage")) - // mage -> magi - return name.substr(0, name.length() - 1) + "i"; - else if ( ends_with(name, "sheep") || ends_with(name, "manes") - || ends_with(name, "fish") ) - // Maybe we should generalise 'manes' to ends_with("es")? - return name; - else if (ends_with(name, "ch") || ends_with(name, "sh") - || ends_with(name, "x")) - // To handle cockroaches, fish and sphinxes. Fish will be netted by - // the previous check anyway. - return name + "es"; - else if (ends_with(name, "um")) - // simulacrum -> simulacra - return name.substr(0, name.length() - 2) + "a"; - else if (ends_with(name, "efreet")) - // efreet -> efreeti. Not sure this is correct. - return name + "i"; - - return name + "s"; -} - // Naively prefix A/an to a monster name. At the moment, we don't have monster // names that demand more sophistication (maybe ynoxinul - don't know how // that's pronounced). diff --git a/crawl-ref/source/Kills.h b/crawl-ref/source/Kills.h index 81f82be2f5..ea02322742 100644 --- a/crawl-ref/source/Kills.h +++ b/crawl-ref/source/Kills.h @@ -12,8 +12,6 @@ #include <stdio.h> #include "enum.h" -std::string pluralise(const std::string &name, - const char *no_of[] = NULL); std::string apostrophise(const std::string &name); struct monsters; diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index dfeaefb35c..8e6407e8d1 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -422,7 +422,7 @@ static void handle_wizard_command( void ) case 'v': // this command isn't very exciting... feel free to replace - i = prompt_invent_item( "Value of which item?", MT_INVSELECT, -1 ); + i = prompt_invent_item( "Value of which item?", MT_INVLIST, -1 ); if (i == PROMPT_ABORT || !is_random_artefact( you.inv[i] )) { canned_msg( MSG_OK ); @@ -436,7 +436,7 @@ static void handle_wizard_command( void ) case '+': i = prompt_invent_item( - "Make an artefact out of which item?", MT_INVSELECT, -1 ); + "Make an artefact out of which item?", MT_INVLIST, -1 ); if (i == PROMPT_ABORT) { canned_msg( MSG_OK ); diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc index 16cfd3be7a..247230c3cd 100644 --- a/crawl-ref/source/command.cc +++ b/crawl-ref/source/command.cc @@ -143,7 +143,7 @@ static void adjust_item(void) return; } - from_slot = prompt_invent_item( "Adjust which item?", MT_INVSELECT, -1 ); + from_slot = prompt_invent_item( "Adjust which item?", MT_INVLIST, -1 ); if (from_slot == PROMPT_ABORT) { canned_msg( MSG_OK ); @@ -154,7 +154,7 @@ static void adjust_item(void) to_slot = prompt_invent_item( "Adjust to which letter?", - MT_INVSELECT, + MT_INVLIST, -1, false, false ); diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index a24e66a01a..d84590d1e6 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -791,7 +791,7 @@ void tweak_object(void) char specs[50]; char keyin; - int item = prompt_invent_item("Tweak which item? ", MT_INVSELECT, -1); + int item = prompt_invent_item("Tweak which item? ", MT_INVLIST, -1); if (item == PROMPT_ABORT) { canned_msg( MSG_OK ); diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 3f930a509b..06415f0a76 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -904,7 +904,12 @@ enum description_level_type DESC_PLAIN, // 6 DESC_NOCAP_ITS, // 7 DESC_INVENTORY_EQUIP, // 8 - DESC_INVENTORY // 9 + DESC_INVENTORY, // 9 + + // Partial item names. + DESC_BASENAME, // Base name of item subtype + DESC_QUALNAME // Name without articles, quantities, + // enchantments. }; enum dragon_class_type @@ -1722,9 +1727,12 @@ enum map_section_type // see maps.cc and dungeon.cc {dlb} enum menu_type { - MT_INVSELECT, // General - select single item + MT_ANY = -1, + MT_INVLIST, // List inventory - MT_DROP + MT_DROP, + + MT_PICKUP }; // if you mess with this list, you'll need to make changes in initfile.cc diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index 065a2961ca..05adcbe314 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -442,7 +442,8 @@ public: *this = item_def(); } private: - std::string name_aux( bool terse, bool ident ) const; + std::string name_aux( description_level_type desc, + bool terse, bool ident ) const; }; class input_history @@ -1241,6 +1242,43 @@ struct feature_override feature_def override; }; +class InvEntry; +typedef int (*item_sort_fn)(const InvEntry *a, const InvEntry *b); +struct item_comparator +{ + item_sort_fn cmpfn; + bool negated; + + item_comparator(item_sort_fn cfn, bool neg = false) + : cmpfn(cfn), negated(neg) + { + } + int compare(const InvEntry *a, const InvEntry *b) const + { + return (negated? -cmpfn(a, b) : cmpfn(a, b)); + } +}; +typedef std::vector<item_comparator> item_sort_comparators; + +struct menu_sort_condition +{ +public: + menu_type mtype; + int sort; + item_sort_comparators cmp; + +public: + menu_sort_condition(menu_type mt = MT_INVLIST, int sort = 0); + menu_sort_condition(const std::string &s); + + bool matches(menu_type mt) const; + +private: + void set_menu_type(std::string &s); + void set_sort(std::string &s); + void set_comparators(std::string &s); +}; + class InitLineInput; struct game_options { @@ -1394,8 +1432,7 @@ public: std::vector<colour_mapping> menu_colour_mappings; std::vector<message_colour_mapping> message_colour_mappings; - int sort_menus; // 0 = always, -1 = never, number = beyond - // that size. + std::vector<menu_sort_condition> sort_menus; int dump_kill_places; // How to dump place information for kills. int dump_message_count; // How many old messages to dump @@ -1522,6 +1559,7 @@ private: const std::string &interrupt_names, bool append_interrupts, bool remove_interrupts); + void set_menu_sort(std::string field); void new_dump_fields(const std::string &text, bool add = true); void do_kill_map(const std::string &from, const std::string &to); int read_explore_stop_conditions(const std::string &) const; diff --git a/crawl-ref/source/food.cc b/crawl-ref/source/food.cc index dfb90d4591..749b74d29e 100644 --- a/crawl-ref/source/food.cc +++ b/crawl-ref/source/food.cc @@ -398,7 +398,7 @@ bool prompt_eat_from_inventory(void) int which_inventory_slot = prompt_invent_item( "Eat which item?", - MT_INVSELECT, + MT_INVLIST, OBJ_FOOD, true, true, true, 0, NULL, OPER_EAT ); diff --git a/crawl-ref/source/initfile.cc b/crawl-ref/source/initfile.cc index 81787d8cd8..d31faf07a4 100644 --- a/crawl-ref/source/initfile.cc +++ b/crawl-ref/source/initfile.cc @@ -28,6 +28,7 @@ #include "Kills.h" #include "files.h" #include "defines.h" +#include "invent.h" #include "libutil.h" #include "player.h" #include "stash.h" @@ -619,8 +620,9 @@ void game_options::reset_options() travel_stair_cost = 500; travel_exclude_radius2 = 68; - // Don't sort menus by default. - sort_menus = -1; + // Sort only pickup menus by default. + sort_menus.clear(); + set_menu_sort("pickup: true"); tc_reachable = BLUE; tc_excluded = LIGHTMAGENTA; @@ -1303,6 +1305,17 @@ void game_options::add_message_colour_mapping(const std::string &field) message_colour_mappings.push_back( m ); } +// Option syntax is: +// sort_menu = [menu_type:]yes|no|auto:n[:sort_conditions] +void game_options::set_menu_sort(std::string field) +{ + if (field.empty()) + return; + + menu_sort_condition cond(field); + sort_menus.push_back(cond); +} + void game_options::read_option_line(const std::string &str, bool runscript) { std::string key = ""; @@ -2042,7 +2055,13 @@ void game_options::read_option_line(const std::string &str, bool runscript) #endif // WIZARD else if (key == "sort_menus") { - sort_menus = read_bool_or_number(field, sort_menus, "auto:"); + std::vector<std::string> frags = split_string(";", field); + for (int i = 0, size = frags.size(); i < size; ++i) + { + if (frags[i].empty()) + continue; + set_menu_sort(frags[i]); + } } else if (key == "travel_delay") { @@ -2700,3 +2719,77 @@ int game_options::o_colour(const char *name, int def) const int col = str_to_colour(val); return (col == -1? def : col); } + +/////////////////////////////////////////////////////////////////////// +// menu_sort_condition + +menu_sort_condition::menu_sort_condition(menu_type _mt, int _sort) + : mtype(_mt), sort(_sort), cmp() +{ +} + +menu_sort_condition::menu_sort_condition(const std::string &s) + : mtype(MT_ANY), sort(-1), cmp() +{ + std::string cp = s; + set_menu_type(cp); + set_sort(cp); + set_comparators(cp); +} + +bool menu_sort_condition::matches(menu_type mt) const +{ + return (mtype == MT_ANY || mtype == mt); +} + +void menu_sort_condition::set_menu_type(std::string &s) +{ + static struct + { + const std::string mname; + menu_type mtype; + } menu_type_map[] = + { + { "any:", MT_ANY }, + { "inv:", MT_INVLIST }, + { "drop:", MT_DROP }, + { "pickup:", MT_PICKUP } + }; + + for (unsigned mi = 0; mi < ARRAYSIZE(menu_type_map); ++mi) + { + const std::string &name = menu_type_map[mi].mname; + if (s.find(name) == 0) + { + s = s.substr(name.length()); + mtype = menu_type_map[mi].mtype; + break; + } + } +} + +void menu_sort_condition::set_sort(std::string &s) +{ + // Strip off the optional sort clauses and get the primary sort condition. + std::string::size_type trail_pos = s.find(':'); + if (s.find("auto:") == 0) + trail_pos = s.find(':', trail_pos + 1); + + std::string sort_cond = + trail_pos == std::string::npos? s : s.substr(0, trail_pos); + + trim_string(sort_cond); + sort = read_bool_or_number(sort_cond, sort, "auto:"); + + if (trail_pos != std::string::npos) + s = s.substr(trail_pos + 1); + else + s.clear(); +} + +void menu_sort_condition::set_comparators(std::string &s) +{ + init_item_sort_comparators( + cmp, + s.empty()? "basename, qualname, curse, qty" : s); +} diff --git a/crawl-ref/source/invent.cc b/crawl-ref/source/invent.cc index 18773c5643..e438d15ab1 100644 --- a/crawl-ref/source/invent.cc +++ b/crawl-ref/source/invent.cc @@ -82,6 +82,30 @@ InvEntry::InvEntry( const item_def &i ) : MenuEntry( "", MEL_ITEM ), item( &i ) quantity = i.quantity; } +const std::string &InvEntry::get_basename() const +{ + if (basename.empty()) + basename = item->name(DESC_BASENAME); + return (basename); +} + +const std::string &InvEntry::get_qualname() const +{ + if (qualname.empty()) + qualname = item->name(DESC_QUALNAME); + return (qualname); +} + +const std::string &InvEntry::get_fullname() const +{ + return (text); +} + +const bool InvEntry::is_item_cursed() const +{ + return (item_ident(*item, ISFLAG_KNOW_CURSE) && item_cursed(*item)); +} + std::string InvEntry::get_text() const { std::ostringstream tstr; @@ -259,9 +283,132 @@ void InvMenu::load_inv_items(int item_selector, } } -static bool compare_menu_entries(const MenuEntry* a, const MenuEntry* b) +template <std::string (*proc)(const InvEntry *a)> +int compare_item_str(const InvEntry *a, const InvEntry *b) +{ + return (proc(a).compare(proc(b))); +} + +template <typename T, T (*proc)(const InvEntry *a)> +int compare_item(const InvEntry *a, const InvEntry *b) +{ + return (int(proc(a)) - int(proc(b))); +} + +// C++ needs anonymous subs already! +std::string sort_item_basename(const InvEntry *a) +{ + return a->get_basename(); +} +std::string sort_item_qualname(const InvEntry *a) +{ + return a->get_qualname(); +} +std::string sort_item_fullname(const InvEntry *a) +{ + return a->get_fullname(); +} +int sort_item_qty(const InvEntry *a) +{ + return a->quantity; +} +int sort_item_slot(const InvEntry *a) +{ + return a->item->link; +} +bool sort_item_curse(const InvEntry *a) +{ + return a->is_item_cursed(); +} + +static bool compare_invmenu_items(const InvEntry *a, const InvEntry *b, + const item_sort_comparators *cmps) +{ + for (item_sort_comparators::const_iterator i = cmps->begin(); + i != cmps->end(); + ++i) + { + const int cmp = i->compare(a, b); + if (cmp) + return (cmp < 0); + } + return (false); +} + +struct menu_entry_comparator +{ + const menu_sort_condition *cond; + + menu_entry_comparator(const menu_sort_condition *c) + : cond(c) + { + } + + bool operator () (const MenuEntry* a, const MenuEntry* b) const + { + const InvEntry *ia = dynamic_cast<const InvEntry *>(a); + const InvEntry *ib = dynamic_cast<const InvEntry *>(b); + return compare_invmenu_items(ia, ib, &cond->cmp); + } +}; + +void init_item_sort_comparators(item_sort_comparators &list, + const std::string &set) { - return (*a < *b); + static struct + { + const std::string cname; + item_sort_fn cmp; + } cmp_map[] = + { + { "basename", compare_item_str<sort_item_basename> }, + { "qualname", compare_item_str<sort_item_qualname> }, + { "fullname", compare_item_str<sort_item_fullname> }, + { "curse", compare_item<bool, sort_item_curse> }, + { "qty", compare_item<int, sort_item_qty> }, + { "slot", compare_item<int, sort_item_slot> }, + }; + + list.clear(); + std::vector<std::string> cmps = split_string(",", set); + for (int i = 0, size = cmps.size(); i < size; ++i) + { + std::string s = cmps[i]; + if (s.empty()) + continue; + + const bool negated = s[0] == '>'; + if (s[0] == '<' || s[0] == '>') + s = s.substr(1); + + for (unsigned ci = 0; ci < ARRAYSIZE(cmp_map); ++ci) + if (cmp_map[ci].cname == s) + { + list.push_back( item_comparator( cmp_map[ci].cmp, negated ) ); + break; + } + } + + if (list.empty()) + list.push_back( + item_comparator(compare_item_str<sort_item_fullname>)); +} + +const menu_sort_condition *InvMenu::find_menu_sort_condition() const +{ + for (int i = 0, size = Options.sort_menus.size(); i < size; ++i) + if (Options.sort_menus[i].matches(type)) + return &Options.sort_menus[i]; + return (NULL); +} + +void InvMenu::sort_menu(std::vector<InvEntry*> &invitems, + const menu_sort_condition *cond) +{ + if (!cond || cond->sort == -1 || (int) invitems.size() < cond->sort) + return; + + std::sort( invitems.begin(), invitems.end(), menu_entry_comparator(cond) ); } void InvMenu::load_items(const std::vector<const item_def*> &mitems, @@ -274,6 +421,8 @@ void InvMenu::load_items(const std::vector<const item_def*> &mitems, menu_letter ckey; std::vector<InvEntry*> items_in_class; + const menu_sort_condition *cond = find_menu_sort_condition(); + for (int i = 0; i < NUM_OBJECT_CLASSES; ++i) { if (!inv_class[i]) continue; @@ -287,10 +436,7 @@ void InvMenu::load_items(const std::vector<const item_def*> &mitems, items_in_class.push_back( new InvEntry(*mitems[j]) ); } - if (Options.sort_menus != -1 && - (int)items_in_class.size() >= Options.sort_menus) - std::sort( items_in_class.begin(), items_in_class.end(), - compare_menu_entries ); + sort_menu(items_in_class, cond); for (unsigned int j = 0; j < items_in_class.size(); ++j) { @@ -432,12 +578,14 @@ std::string item_class_name( int type, bool terse ) } std::vector<SelItem> select_items( const std::vector<const item_def*> &items, - const char *title, bool noselect ) + const char *title, bool noselect, + menu_type mtype ) { std::vector<SelItem> selected; if (!items.empty()) { InvMenu menu; + menu.set_type(mtype); menu.set_title(title); menu.load_items(items); menu.set_flags(noselect ? MF_NOSELECT : MF_MULTISELECT); diff --git a/crawl-ref/source/invent.h b/crawl-ref/source/invent.h index 882b340a25..4278fa222a 100644 --- a/crawl-ref/source/invent.h +++ b/crawl-ref/source/invent.h @@ -56,6 +56,9 @@ private: static bool show_prices; static void set_show_prices(bool doshow); + mutable std::string basename; + mutable std::string qualname; + friend class InvShowPrices; public: @@ -64,6 +67,11 @@ public: InvEntry( const item_def &i ); std::string get_text() const; + const std::string &get_basename() const; + const std::string &get_qualname() const; + const std::string &get_fullname() const; + const bool is_item_cursed() const; + private: void add_class_hotkeys(const item_def &i); }; @@ -79,7 +87,7 @@ class InvMenu : public Menu { public: InvMenu(int mflags = MF_MULTISELECT) - : Menu(mflags), type(MT_INVSELECT), pre_select(NULL), + : Menu(mflags), type(MT_INVLIST), pre_select(NULL), title_annotate(NULL) { } @@ -119,6 +127,9 @@ public: protected: bool process_key(int key); void do_preselect(InvEntry *ie); + void sort_menu(std::vector<InvEntry*> &items, + const menu_sort_condition *cond); + const menu_sort_condition *find_menu_sort_condition() const; protected: menu_type type; @@ -140,7 +151,8 @@ int prompt_invent_item( const char *prompt, std::vector<SelItem> select_items( const std::vector<const item_def*> &items, - const char *title, bool noselect = false ); + const char *title, bool noselect = false, + menu_type mtype = MT_PICKUP ); std::vector<SelItem> prompt_invent_items( const char *prompt, @@ -187,4 +199,7 @@ std::string item_class_name(int type, bool terse = false); bool check_warning_inscriptions(const item_def& item, operation_types oper); bool has_warning_inscription(const item_def& item, operation_types oper); +void init_item_sort_comparators(item_sort_comparators &list, + const std::string &set); + #endif diff --git a/crawl-ref/source/item_use.cc b/crawl-ref/source/item_use.cc index afb24beeb7..80cd805a18 100644 --- a/crawl-ref/source/item_use.cc +++ b/crawl-ref/source/item_use.cc @@ -209,7 +209,7 @@ bool wield_weapon(bool auto_wield, int slot, bool show_weff_messages) if (!auto_wield) item_slot = prompt_invent_item( "Wield which item (- for none, * to show all)?", - MT_INVSELECT, OSEL_WIELD, + MT_INVLIST, OSEL_WIELD, true, true, true, '-', NULL, OPER_WIELD); else item_slot = PROMPT_GOT_SPECIAL; @@ -650,7 +650,7 @@ bool armour_prompt( const std::string & mesg, int *index, operation_types oper) canned_msg(MSG_TOO_BERSERK); else { - slot = prompt_invent_item( mesg.c_str(), MT_INVSELECT, OBJ_ARMOUR, + slot = prompt_invent_item( mesg.c_str(), MT_INVLIST, OBJ_ARMOUR, true, true, true, 0, NULL, oper ); @@ -1069,7 +1069,7 @@ void throw_anything(void) } throw_slot = prompt_invent_item( "Throw which item? (* to show all)", - MT_INVSELECT, + MT_INVLIST, OBJ_MISSILES, true, true, true, 0, NULL, OPER_THROW ); if (throw_slot == PROMPT_ABORT) @@ -2286,7 +2286,7 @@ bool puton_ring(int slot, bool prompt_finger) item_slot = slot; else item_slot = prompt_invent_item( "Put on which piece of jewellery?", - MT_INVSELECT, OBJ_JEWELLERY ); + MT_INVLIST, OBJ_JEWELLERY ); if (item_slot == PROMPT_ABORT) { @@ -2426,7 +2426,7 @@ bool remove_ring(int slot, bool announce) { const int equipn = slot == -1? prompt_invent_item( "Remove which piece of jewellery?", - MT_INVSELECT, + MT_INVLIST, OBJ_JEWELLERY, true, true, true, 0, NULL, OPER_REMOVE) : slot; @@ -2518,7 +2518,7 @@ void zap_wand(void) } item_slot = prompt_invent_item( "Zap which item?", - MT_INVSELECT, + MT_INVLIST, OBJ_WANDS, true, true, true, 0, NULL, OPER_ZAP ); @@ -2687,7 +2687,7 @@ void inscribe_item() } item_slot = prompt_invent_item( "Inscribe which item? ", - MT_INVSELECT, + MT_INVLIST, OSEL_ANY ); if (item_slot == PROMPT_ABORT) { @@ -2731,7 +2731,7 @@ void drink(void) } item_slot = prompt_invent_item( "Drink which item?", - MT_INVSELECT, OBJ_POTIONS, + MT_INVLIST, OBJ_POTIONS, true, true, true, 0, NULL, OPER_QUAFF ); if (item_slot == PROMPT_ABORT) @@ -3227,7 +3227,7 @@ void read_scroll(void) int item_slot = prompt_invent_item( "Read which item?", - MT_INVSELECT, + MT_INVLIST, OBJ_SCROLLS ); if (item_slot == PROMPT_ABORT) { @@ -3585,7 +3585,7 @@ void read_scroll(void) void examine_object(void) { int item_slot = prompt_invent_item( "Examine which item?", - MT_INVSELECT, -1, + MT_INVLIST, -1, true, true, true, 0, NULL, OPER_EXAMINE ); if (item_slot == PROMPT_ABORT) diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc index 63ee563734..44b7095f7c 100644 --- a/crawl-ref/source/itemname.cc +++ b/crawl-ref/source/itemname.cc @@ -88,7 +88,7 @@ std::string item_def::name(description_level_type descrip, { std::ostringstream buff; - const std::string auxname = this->name_aux(terse, ident); + const std::string auxname = this->name_aux(descrip, terse, ident); const bool startvowel = is_vowel(auxname[0]); if (descrip == DESC_INVENTORY_EQUIP || descrip == DESC_INVENTORY) @@ -154,7 +154,8 @@ std::string item_def::name(description_level_type descrip, break; } - buff << this->quantity << " "; + if (descrip != DESC_BASENAME && descrip != DESC_QUALNAME) + buff << this->quantity << " "; } else { @@ -904,16 +905,25 @@ static void output_with_sign(std::ostream& os, int val) // Note that "terse" is only currently used for the "in hand" listing on // the game screen. -std::string item_def::name_aux( bool terse, bool ident ) const +std::string item_def::name_aux( description_level_type desc, + bool terse, bool ident ) const { // Shortcuts const int item_typ = this->sub_type; const int it_plus = this->plus; const int item_plus2 = this->plus2; - const bool know_curse = ident || item_ident(*this, ISFLAG_KNOW_CURSE); + const bool basename = desc == DESC_BASENAME; + const bool qualname = desc == DESC_QUALNAME; + + const bool know_curse = + !basename && !qualname + && (ident || item_ident(*this, ISFLAG_KNOW_CURSE)); + const bool know_type = ident || item_type_known(*this); - const bool know_pluses = ident || item_ident(*this, ISFLAG_KNOW_PLUSES); + const bool know_pluses = + !basename && !qualname + && (ident || item_ident(*this, ISFLAG_KNOW_PLUSES)); bool need_plural = true; int brand; @@ -965,7 +975,7 @@ std::string item_def::name_aux( bool terse, bool ident ) const // Now that we can have "glowing elven" weapons, it's // probably a good idea to cut out the descriptive // term once it's become obsolete. -- bwr - if (!know_pluses && !terse) + if (!know_pluses && !terse && !basename) { switch (get_equip_desc( *this )) { @@ -978,30 +988,35 @@ std::string item_def::name_aux( bool terse, bool ident ) const } } - // always give racial type (it does have game effects) - buff << racial_description_string(*this, terse); + if (!basename) + { + // always give racial type (it does have game effects) + buff << racial_description_string(*this, terse); - if (know_type && !terse && - (get_weapon_brand(*this) == SPWPN_VAMPIRICISM)) - buff << "vampiric "; + if (know_type && !terse && + (get_weapon_brand(*this) == SPWPN_VAMPIRICISM)) + buff << "vampiric "; - buff << item_base_name(*this); - if ( know_type ) - buff << weapon_brand_name(*this, terse); + buff << item_base_name(*this); + if ( know_type ) + buff << weapon_brand_name(*this, terse); - if (know_curse && item_cursed(*this) && terse) - buff << " (curse)"; + if (know_curse && item_cursed(*this) && terse) + buff << " (curse)"; + } break; case OBJ_MISSILES: - need_plural = false; brand = get_ammo_brand( *this ); - if (brand == SPMSL_POISONED) - buff << ((terse) ? "poison " : "poisoned "); + if (!basename) + { + if (brand == SPMSL_POISONED) + buff << ((terse) ? "poison " : "poisoned "); - if (brand == SPMSL_CURARE) - buff << ((terse) ? "curare " : "curare-tipped "); + if (brand == SPMSL_CURARE) + buff << ((terse) ? "curare " : "curare-tipped "); + } if (know_pluses) { @@ -1022,10 +1037,7 @@ std::string item_def::name_aux( bool terse, bool ident ) const default: buff << "hysterical raisin"; break; } - if (this->quantity > 1) - buff << "s"; - - if (know_type) + if (know_type && !basename) { switch (brand) { @@ -1063,7 +1075,8 @@ std::string item_def::name_aux( bool terse, bool ident ) const if (item_typ == ARM_GLOVES || item_typ == ARM_BOOTS) buff << "pair of "; - if (is_random_artefact( *this )) + // When asking for the base item name, randartism is ignored. + if (is_random_artefact( *this ) && !basename) { buff << randart_armour_name(*this); break; @@ -1072,7 +1085,7 @@ std::string item_def::name_aux( bool terse, bool ident ) const // Now that we can have "glowing elven" armour, it's // probably a good idea to cut out the descriptive // term once it's become obsolete. -- bwr - if (!know_pluses && !terse) + if (!know_pluses && !terse & !basename) { switch (get_equip_desc( *this )) { @@ -1099,11 +1112,15 @@ std::string item_def::name_aux( bool terse, bool ident ) const } } - // always give racial description (has game effects) - buff << racial_description_string(*this, terse); + if (!basename) + { + // always give racial description (has game effects) + buff << racial_description_string(*this, terse); + } buff << item_base_name(*this); + if (!basename) { const special_armour_type sparm = get_armour_ego_type( *this ); @@ -1115,7 +1132,7 @@ std::string item_def::name_aux( bool terse, bool ident ) const } } - if (know_curse && item_cursed(*this) && terse) + if (know_curse && item_cursed(*this) && terse && !basename) buff << " (curse)"; break; @@ -1136,13 +1153,9 @@ std::string item_def::name_aux( bool terse, bool ident ) const break; case OBJ_POTIONS: - need_plural = false; if (know_type) { - buff << "potion"; - if ( this->quantity > 1 ) - buff << "s"; - buff << " of " << potion_type_name(item_typ); + buff << "potion of " << potion_type_name(item_typ); } else { @@ -1174,9 +1187,6 @@ std::string item_def::name_aux( bool terse, bool ident ) const : potion_colours[pcolour]; buff << qualifier << clr << " potion"; - - if (this->quantity > 1) - buff << "s"; } break; @@ -1189,50 +1199,35 @@ std::string item_def::name_aux( bool terse, bool ident ) const case FOOD_APPLE: buff << "apple"; break; case FOOD_CHOKO: buff << "choko"; break; case FOOD_HONEYCOMB: buff << "honeycomb"; break; - case FOOD_ROYAL_JELLY: buff << "royal jell"; break; + case FOOD_ROYAL_JELLY: buff << "royal jelly"; break; case FOOD_SNOZZCUMBER: buff << "snozzcumber"; break; case FOOD_PIZZA: buff << "slice of pizza"; break; case FOOD_APRICOT: buff << "apricot"; break; case FOOD_ORANGE: buff << "orange"; break; case FOOD_BANANA: buff << "banana"; break; - case FOOD_STRAWBERRY: buff << "strawberr"; break; + case FOOD_STRAWBERRY: buff << "strawberry"; break; case FOOD_RAMBUTAN: buff << "rambutan"; break; case FOOD_LEMON: buff << "lemon"; break; case FOOD_GRAPE: buff << "grape"; break; case FOOD_SULTANA: buff << "sultana"; break; case FOOD_LYCHEE: buff << "lychee"; break; - case FOOD_BEEF_JERKY: buff << "beef jerk"; break; + case FOOD_BEEF_JERKY: buff << "beef jerky"; break; case FOOD_CHEESE: buff << "cheese"; break; case FOOD_SAUSAGE: buff << "sausage"; break; case FOOD_CHUNK: - { - need_plural = false; - if (this->special < 100) buff << "rotting "; - buff << "chunk"; - - if (this->quantity > 1) - buff << "s"; - - buff << " of " << mons_type_name(it_plus, DESC_PLAIN) << " flesh"; + buff << "chunk of " + << mons_type_name(it_plus, DESC_PLAIN) + << " flesh"; break; } - } - - if (item_typ == FOOD_ROYAL_JELLY || item_typ == FOOD_STRAWBERRY - || item_typ == FOOD_BEEF_JERKY) - buff << ((this->quantity > 1) ? "ie" : "y"); break; case OBJ_SCROLLS: - need_plural = false; - buff << "scroll"; - if ( this->quantity > 1 ) - buff << "s"; - buff << " "; + buff << "scroll "; if (know_type) { @@ -1270,7 +1265,7 @@ std::string item_def::name_aux( bool terse, bool ident ) const } } - if (is_random_artefact( *this )) + if (is_random_artefact( *this ) && !basename) { buff << randart_jewellery_name(*this); break; @@ -1278,7 +1273,7 @@ std::string item_def::name_aux( bool terse, bool ident ) const if (know_type) { - if (know_pluses && ring_has_pluses(*this)) + if (know_pluses && ring_has_pluses(*this) && !basename && !qualname) { output_with_sign(buff, it_plus); @@ -1310,14 +1305,10 @@ std::string item_def::name_aux( bool terse, bool ident ) const break; case OBJ_MISCELLANY: - need_plural = false; if ( item_typ == MISC_RUNE_OF_ZOT ) { buff << rune_type_name(it_plus) << " rune"; - if (this->quantity > 1) - buff << "s"; - if (know_type) buff << " of Zot"; } @@ -1409,8 +1400,12 @@ std::string item_def::name_aux( bool terse, bool ident ) const buff << "!"; } + // One plural to rule them all. + if (need_plural && this->quantity > 1 && !basename && !qualname) + buff.str( pluralise(buff.str()) ); + // Disambiguation - if (!terse && know_type) + if (!terse && !basename && know_type) { switch (this->base_type) { @@ -1465,9 +1460,6 @@ std::string item_def::name_aux( bool terse, bool ident ) const << ",qu:" << this->quantity << ")"; } - if (need_plural && this->quantity > 1) - buff << "s"; - return buff.str(); } diff --git a/crawl-ref/source/libutil.cc b/crawl-ref/source/libutil.cc index 6bf33921ba..bee74bab03 100644 --- a/crawl-ref/source/libutil.cc +++ b/crawl-ref/source/libutil.cc @@ -212,6 +212,60 @@ int ends_with(const std::string &s, const char *suffixes[]) return (0); } +// Pluralises a monster or item name. This'll need to be updated for +// correctness whenever new monsters/items are added. +std::string pluralise(const std::string &name, + const char *no_of[]) +{ + std::string::size_type pos; + + // Pluralise first word of names like 'eye of draining' or + // 'scrolls labeled FOOBAR', but only if the whole name is not + // suffixed by a supplied modifier, such as 'zombie' or 'skeleton' + if ( (pos = name.find(" of ")) != std::string::npos + && !ends_with(name, no_of) ) + return pluralise(name.substr(0, pos)) + name.substr(pos); + else if ( (pos = name.find(" labeled ")) != std::string::npos + && !ends_with(name, no_of) ) + return pluralise(name.substr(0, pos)) + name.substr(pos); + else if (ends_with(name, "us")) + // Fungus, ufetubus, for instance. + return name.substr(0, name.length() - 2) + "i"; + else if (ends_with(name, "larva") || ends_with(name, "amoeba")) + // Giant amoebae sounds a little weird, to tell the truth. + return name + "e"; + else if (ends_with(name, "ex")) + // Vortex; vortexes is legal, but the classic plural is cooler. + return name.substr(0, name.length() - 2) + "ices"; + else if (ends_with(name, "cyclops")) + return name.substr(0, name.length() - 1) + "es"; + else if (ends_with(name, "y")) + return name.substr(0, name.length() - 1) + "ies"; + else if (ends_with(name, "elf") || ends_with(name, "olf")) + // Elf, wolf. Dwarfs can stay dwarfs, if there were dwarfs. + return name.substr(0, name.length() - 1) + "ves"; + else if (ends_with(name, "mage")) + // mage -> magi + return name.substr(0, name.length() - 1) + "i"; + else if ( ends_with(name, "sheep") || ends_with(name, "manes") + || ends_with(name, "fish") ) + // Maybe we should generalise 'manes' to ends_with("es")? + return name; + else if (ends_with(name, "ch") || ends_with(name, "sh") + || ends_with(name, "x")) + // To handle cockroaches, fish and sphinxes. Fish will be netted by + // the previous check anyway. + return name + "es"; + else if (ends_with(name, "um")) + // simulacrum -> simulacra + return name.substr(0, name.length() - 2) + "a"; + else if (ends_with(name, "efreet")) + // efreet -> efreeti. Not sure this is correct. + return name + "i"; + + return name + "s"; +} + std::string replace_all(std::string s, const std::string &find, const std::string &repl) diff --git a/crawl-ref/source/libutil.h b/crawl-ref/source/libutil.h index a36d632c81..63746ee8b6 100644 --- a/crawl-ref/source/libutil.h +++ b/crawl-ref/source/libutil.h @@ -30,6 +30,8 @@ int unmangle_direction_keys(int keyin, int keymap = 0, void lowercase(std::string &s); void uppercase(std::string &s); bool ends_with(const std::string &s, const std::string &suffix); +std::string pluralise(const std::string &name, + const char *no_of[] = NULL); /** * Returns 1 + the index of the first suffix that matches the given string, diff --git a/crawl-ref/source/spells1.cc b/crawl-ref/source/spells1.cc index 50115801be..555c81ebf1 100644 --- a/crawl-ref/source/spells1.cc +++ b/crawl-ref/source/spells1.cc @@ -368,7 +368,7 @@ void identify(int power) do { - item_slot = prompt_invent_item( "Identify which item?", MT_INVSELECT, + item_slot = prompt_invent_item( "Identify which item?", MT_INVLIST, OSEL_UNIDENT, true, true, false ); if (item_slot == PROMPT_ABORT) { diff --git a/crawl-ref/source/spells4.cc b/crawl-ref/source/spells4.cc index 21b8a6f68c..55c9379973 100644 --- a/crawl-ref/source/spells4.cc +++ b/crawl-ref/source/spells4.cc @@ -1878,7 +1878,7 @@ void cast_evaporate(int pow) struct bolt beem; const int potion = - prompt_invent_item( "Throw which potion?", MT_INVSELECT, OBJ_POTIONS ); + prompt_invent_item( "Throw which potion?", MT_INVLIST, OBJ_POTIONS ); if (potion == -1) { diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index d9b9652ac4..507fa1b4a6 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -959,7 +959,7 @@ static bool which_spellbook( int &book, int &spell ) mprf("You can memorise %d more level%s of spells.", avail_levels, (avail_levels > 1) ? "s" : "" ); - book = prompt_invent_item("Memorise from which spellbook?", MT_INVSELECT, + book = prompt_invent_item("Memorise from which spellbook?", MT_INVLIST, OBJ_BOOKS ); if (book == PROMPT_ABORT) { diff --git a/crawl-ref/source/stash.cc b/crawl-ref/source/stash.cc index f38d646473..5e11499b82 100644 --- a/crawl-ref/source/stash.cc +++ b/crawl-ref/source/stash.cc @@ -361,7 +361,10 @@ std::string Stash::stash_item_name(const item_def &item) class StashMenu : public InvMenu { public: - StashMenu() : InvMenu(MF_SINGLESELECT), can_travel(false) { } + StashMenu() : InvMenu(MF_SINGLESELECT), can_travel(false) + { + set_type(MT_PICKUP); + } unsigned char getkey() const; public: bool can_travel; |