summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-05-31 19:25:30 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2007-05-31 19:25:30 +0000
commit7c5e54508666b65c7890ddc45880f64b38f887cb (patch)
tree452f6a59973c7f7236a2f9fb1f3b39c76eb088c9
parent8df9960b2b34f903658b041751ef4a8c47338c10 (diff)
downloadcrawl-ref-7c5e54508666b65c7890ddc45880f64b38f887cb.tar.gz
crawl-ref-7c5e54508666b65c7890ddc45880f64b38f887cb.zip
sort_menus now allows the user to choose what menus to sort, and how to
sort items, at the cost of obfuscating the sort_menus option massively. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@1494 c06c8d41-db1a-0410-9941-cceddc491573
-rw-r--r--crawl-ref/docs/crawl_options.txt81
-rw-r--r--crawl-ref/init.txt2
-rw-r--r--crawl-ref/source/Kills.cc50
-rw-r--r--crawl-ref/source/Kills.h2
-rw-r--r--crawl-ref/source/acr.cc4
-rw-r--r--crawl-ref/source/command.cc4
-rw-r--r--crawl-ref/source/debug.cc2
-rw-r--r--crawl-ref/source/enum.h14
-rw-r--r--crawl-ref/source/externs.h44
-rw-r--r--crawl-ref/source/food.cc2
-rw-r--r--crawl-ref/source/initfile.cc99
-rw-r--r--crawl-ref/source/invent.cc162
-rw-r--r--crawl-ref/source/invent.h19
-rw-r--r--crawl-ref/source/item_use.cc20
-rw-r--r--crawl-ref/source/itemname.cc132
-rw-r--r--crawl-ref/source/libutil.cc54
-rw-r--r--crawl-ref/source/libutil.h2
-rw-r--r--crawl-ref/source/spells1.cc2
-rw-r--r--crawl-ref/source/spells4.cc2
-rw-r--r--crawl-ref/source/spl-book.cc2
-rw-r--r--crawl-ref/source/stash.cc5
21 files changed, 534 insertions, 170 deletions
diff --git a/crawl-ref/docs/crawl_options.txt b/crawl-ref/docs/crawl_options.txt
index 4d4a4fb0f4..d85eb048ce 100644
--- a/crawl-ref/docs/crawl_options.txt
+++ b/crawl-ref/docs/crawl_options.txt
@@ -652,15 +652,78 @@ default_autoprayer = false
to ignore the prayer messages (see the documentation for
runrest_ignore_message).
-sort_menus = (true | false | auto:X)
- When set to true, items are sorted by description in inventory and
- pickup menus. When set to false (the default), items are ordered by
- equipment slot.
- When set to a number - using a syntax of the form
- sort_menus = auto:5
- - items are sorted by their description if the total number of items in
- that category is at least that number; in the example, having 4
- kinds of potions would not sort them, but having 5 would.
+sort_menus = [menu:](true | false | auto:X)[:sort_order]
+ Controls if and how items are sorted in inventory and pickup
+ menus.
+
+ When sort_menus = false (the default), items are not sorted,
+ and will be ordered by inventory letter (or in the order
+ they're stacked for items on the floor).
+
+ When sort_menus = true, items are sorted by base name,
+ qualified name, curse status and quantity.
+
+ If sort_menus = auto:X, items are sorted if there are at least
+ X items in the same category. For instance:
+ sort_menus = auto:5
+ will sort item classes that have at least 5 items. For
+ instance, having 4 kinds of potions would not sort them, but
+ having 5 would.
+
+ You can explicitly specify sort criteria in the sort_menus
+ option as:
+ sort_menus = true : basename, qualname, curse, qty
+
+ The available sort criteria are:
+
+ * basename:
+ This is the name of the item type. The basename for all of
+ "a +0 robe", "an embroidered robe" and "the cursed +2 robe
+ of Ponies" is just "robe".
+
+ * qualname:
+ The name of the item without articles (a/an/the),
+ quantities, enchantments, or curse-status. The qualified
+ names for the robes described above are "robe", "embroidered
+ robe" and "robe of Ponies", respectively.
+
+ * fullname:
+ This is the name of the item as displayed in menus
+ (including quantities, curse-status, etc.)
+
+ * curse:
+ Curse-status of the item (if known). Uncursed items show up
+ first.
+
+ * qty:
+ The quantity for stackable items (such as scrolls, potions,
+ etc.)
+
+ * slot:
+ The inventory letter for items in inventory; irrelevant for
+ items on the floor.
+
+ The default sort criteria are: "basename, qualname, curse, qty".
+
+ You can ask for a descending order sort by prefixing one or
+ more sort criteria with > as:
+ sort_menus = true : basename, >qty
+
+ You can also request sorting only for specific menus:
+ sort_menus = pickup: true
+ or
+ sort_menus = inv: true
+ (Menu types must be specified as name:, with no space between
+ name and colon.)
+
+ The menu selectors available are:
+
+ pickup: All pickup menus, stash-search menus, etc. for items not
+ in your inventory.
+ drop: The item drop menu.
+ inv: Inventory listings for any command (but not for dropping
+ items).
+ any: All menus; this is the default when unspecified.
4-i Messages and Display Enhancements.
diff --git a/crawl-ref/init.txt b/crawl-ref/init.txt
index 75209994f8..eb392149d7 100644
--- a/crawl-ref/init.txt
+++ b/crawl-ref/init.txt
@@ -150,7 +150,7 @@ stab_brand = hi:blue
# easy_quit_item_prompts = false
# easy_exit_menu = true
# default_autoprayer = true
-# sort_menus = (auto:5 | true | false)
+# sort_menus = pickup: true : basename, qualname, curse, qty
##### 4-i Messages and Display Enhancements #####
#
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;