patch 1783003 (ímproved menu sorting), both by zelgadis.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2041 c06c8d41-db1a-0410-9941-cceddc491573
BW3XFNOS6LDAQLHOZ6RXARCMKCY5JVLVDSXDSSAX4DSYM3FANQBAC
FSD7GIK3YLZXWLEH37BU6KV3IUCFGXPQL6IZ7H65YWNRBEKDBX5AC
WCK6TM2ZD56WSOK6SMMKBYAYGG4OUCTILLMVVLMB4QO5I5S2IVNAC
3C2VE43SHCSBY4LTRTFYFLIPRWFUN6DXU6D34QVWDQTSNRBUFG7AC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
ZJLJGSB2XSBQU42OFQMXL3EG4CXAQGOYAU6YTV2SAWZEJIPFH2CAC
W5VEC2PBIM5DMU5233HOWAZUEPTGWJRZZIA3H35YYQQW6BTP6XUAC
6HQB2N6N75R2RGKJFWRUN7WAC2PNGWQFXTII5DTRLTHZ2BOTMTVAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
IVVTHLTTLOP5TSULXJWUSSXHOKYWVU3OWKYVK45A7RIB6V34MYQAC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
BWAQ3FHBBM6G3K3KYP75CRTR343RDQZJRYX5ZGYUEXYBAC3APDLAC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
ZHFUXYUHS6V47WK2NRH7OU6RX77NRKTXOZC3MND2GG7PEEWSGFTAC
NOB3FIJ2IOGBSK3EKCRS2WORSMTFPFKOQL4V7BTBLENYYTBUPHQQC
L5CVPV5IUBSO4EE3WK4O6SQGIMIEPSMQONFBWEVGJBR2HATLPZIAC
GMSGNBZKUJ3DDTOGLQJ3VUBUYIECAW6PDDHYFPGSOHSG2TUUKJRAC
F4WAQJD7KLOXHZOTOT6WG26PVIDHYNVS3AV6ZNXGAS7PDR4HBWQAC
RVST2QHYJ757ZHK4AUJ5NGPDZ44AD6RVFVXYPKQIBJXZBDNUCHXQC
// mpr() an arbitrarily long list of strings without truncation or risk
// of overflow.
void mpr_comma_separated_list(const std::string prefix,
const std::vector<std::string> list,
const std::string &andc,
const std::string &comma,
const msg_channel_type channel,
const int param)
{
std::string out = prefix;
unsigned width = get_number_of_cols() - 1;
for(int i = 0, size = list.size(); i < size; i++)
{
std::string new_str = list[i];
if (size > 0 && i < (size - 2))
new_str += comma;
else if (i == (size - 2))
new_str += andc;
if (out.length() + new_str.length() >= width)
{
mpr(out.c_str(), channel, param);
out = new_str;
}
else
out += new_str;
}
mpr(out.c_str(), channel, param);
}
}
const bool InvEntry::is_item_glowing() const
{
return (!item_ident(*item, ISFLAG_KNOW_TYPE)
&& (get_equip_desc(*item)
|| (is_artefact(*item)
&& (item->base_type == OBJ_WEAPONS
|| item->base_type == OBJ_MISSILES
|| item->base_type == OBJ_ARMOUR))));
}
const bool InvEntry::is_item_ego() const
{
return (item_ident(*item, ISFLAG_KNOW_TYPE) && !is_artefact(*item)
&& item->special != 0
&& (item->base_type == OBJ_WEAPONS
|| item->base_type == OBJ_MISSILES
|| item->base_type == OBJ_ARMOUR));
}
const bool InvEntry::is_item_art() const
{
return (item_ident(*item, ISFLAG_KNOW_TYPE) && is_artefact(*item));
}
const bool InvEntry::is_item_equipped() const
{
if (item->link == -1 || item->x != -1 || item->y != -1)
return(false);
for (int i = 0; i < NUM_EQUIP; i++)
if (item->link == you.equip[i])
return (true);
return (false);
}
const int InvEntry::item_freshness() const
{
if (item->base_type != OBJ_FOOD || item->sub_type != FOOD_CHUNK)
return 0;
int freshness = item->special - 100;
// Ensure that chunk freshness is never zero, since zero means
// that the item isn't a chunk.
if (freshness >= 0)
freshness++;
// Invert if not a ghoul, so that the freshest chunks will go
// at the top.
if (you.species != SP_GHOUL)
freshness *= -1;
return freshness;
{ "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> },
{ "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> },
{ "glowing", compare_item<bool, sort_item_glowing> },
{ "ego", compare_item<bool, sort_item_ego> },
{ "art", compare_item<bool, sort_item_art> },
{ "equipped", compare_item<bool, sort_item_equipped> },
{ "qty", compare_item<int, sort_item_qty> },
{ "slot", compare_item<int, sort_item_slot> },
{ "freshness", compare_item<int, sort_item_freshness> }
}
#endif
#ifdef WIZARD
static dungeon_feature_type find_appropriate_stairs(bool down)
{
if (you.level_type == LEVEL_DUNGEON)
{
int depth = subdungeon_depth(you.where_are_you, you.your_level);
if (down)
depth++;
else
depth--;
// Can't go down from bottom level of a branch.
if (depth > branches[you.where_are_you].depth)
{
mpr("Can't go down from the bottom of a branch.");
return DNGN_UNSEEN;
}
// Going up from top level of branch
else if (depth == 0)
{
// Special cases
if (you.where_are_you == BRANCH_VESTIBULE_OF_HELL)
return DNGN_EXIT_HELL;
else if (you.where_are_you == BRANCH_MAIN_DUNGEON)
return DNGN_STONE_STAIRS_UP_I;
// General case: look for branch exit and copy it
for (int y = 1; y < GYM; ++y)
{
for (int x = 1; x < GXM; ++x)
{
if (grd[x][y] >= DNGN_RETURN_FROM_ORCISH_MINES &&
grd[x][y] <= DNGN_RETURN_RESERVED_4)
return grd[x][y];
}
}
mpr("Unable to find appropriate branch exit.");
return DNGN_UNSEEN;
}
// Branch non-edge cases
else if (depth >= 1)
{
if (down)
return DNGN_STONE_STAIRS_DOWN_I;
else
return DNGN_ROCK_STAIRS_UP;
}
else
{
mpr("Bug in determing level exit.");
return DNGN_UNSEEN;
}
}
switch(you.level_type)
{
case LEVEL_LABYRINTH:
if (down)
{
// Can't go down in the Labyrinth
mpr("Can't go down in the Labyrinth.");
return DNGN_UNSEEN;
}
else
return DNGN_ROCK_STAIRS_UP;
break;
case LEVEL_ABYSS:
return DNGN_EXIT_ABYSS;
break;
case LEVEL_PANDEMONIUM:
if (down)
return DNGN_TRANSIT_PANDEMONIUM;
else
return DNGN_EXIT_PANDEMONIUM;
break;
case LEVEL_PORTAL_VAULT:
return DNGN_EXIT_PORTAL_VAULT;
break;
default:
mpr("Unknown level type.");
return DNGN_UNSEEN;
}
mpr("Impossible occurence in find_appropriate_stairs()");
return DNGN_UNSEEN;
}
// This lets us, for example, use &U to exit from Pandemonium and
// &D to go to the next level.
command_type real_dir = grid_stair_direction(stairs);
if ((down && real_dir == CMD_GO_UPSTAIRS)
|| (!down && real_dir == CMD_GO_DOWNSTAIRS))
down = !down;
const std::string mname = m->name(DESC_PLAIN, true);
std::string news = (nfound++? ", " : "") + mname;
if (news.length() + mons.length() >= (unsigned) get_number_of_cols() - 1)
{
mpr(mons.c_str());
mons.clear();
news = mname;
}
mons += news;
mons.push_back(m->name(DESC_PLAIN, true));
nfound++;
mprf("I know no traps named \"%s\"", requested_trap);
return;
if (matches.empty())
{
mprf("I know no traps named \"%s\"", requested_trap);
return;
}
// Only one match, use that
else if (matches.size() == 1)
trap = trap_type(matches[0]);
else
{
std::string prefix = "No exact match for trap '";
prefix += requested_trap;
prefix += "', possible matches are: ";
mpr_comma_separated_list(prefix, match_names);
return;
}
mprf("Can't find map named '%s'.", name.c_str());
return;
std::vector<std::string> matches = find_map_matches(name);
if (matches.empty())
{
mprf("Can't find map named '%s'.", name.c_str());
return;
}
else if (matches.size() == 1)
{
std::string prompt = "Only match is '";
prompt += matches[0];
prompt += "', use that?";
if (!yesno(prompt.c_str()))
return;
map = find_map_by_name(matches[0]);
}
else
{
std::string prompt = "No exact matches for '";
prompt += name;
prompt += "', possible matches are: ";
mpr_comma_separated_list(prompt, matches);
return;
}
const dungeon_feature_type feat = dungeon_feature_by_name(specs);
// Accept both "shallow_water" and "Shallow water"
std::string name = lowercase_string(specs);
name = replace_all(name, " ", "_");
dungeon_feature_type feat = dungeon_feature_by_name(name);
mprf(MSGCH_DIAGNOSTICS, "Setting (%d,%d) to %s (%d)",
you.x_pos, you.y_pos, specs, feat);
grd(you.pos()) = feat;
std::vector<std::string> matches =
dungeon_feature_matches(name);
if (matches.empty())
{
mprf(MSGCH_DIAGNOSTICS, "No features matching '%s'",
name.c_str());
return;
}
// Only one possible match, use that.
if (matches.size() == 1)
{
name = matches[0];
feat = dungeon_feature_by_name(name);
}
// Multiple matches, list them to wizard
else
{
std::string prefix = "No exact match for feature '" +
name + "', possible matches are: ";
// Use mpr_comma_separated_list() because the list
// might be *LONG*.
mpr_comma_separated_list(prefix, matches, ", ", " and ",
MSGCH_DIAGNOSTICS);
return;
}
Two items will be compared based on the first sort criteria
where they differ. So with the sort_menus line given above,
if the basenames of two different items are different they
will be alphabetically compared using their basenames; if
the basenames are the same but the qualified names different
it will compare their qualified names, and so on.
The default sort criteria are: "basename, qualname, curse, qty".
* freshness:
The freshness of chunks of meat; irrelevant for everything
else. For non-ghouls it makes the freshest chunks of meat
show up first, and for ghouls the mons rotten chunks of
meat go fist. If this sort criteria is placed before (or
in the absence of) basename and qualname, then non-chunk
food items will be sorted between the non-rotting and
rotting chunks.
The default sort criteria are: "equipped, basename, qualname,
curse, qty".