summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/command.cc
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref/source/command.cc')
-rw-r--r--crawl-ref/source/command.cc504
1 files changed, 487 insertions, 17 deletions
diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc
index 7edb77a71f..581ae33bf2 100644
--- a/crawl-ref/source/command.cc
+++ b/crawl-ref/source/command.cc
@@ -26,7 +26,10 @@
#include "abl-show.h"
#include "chardump.h"
#include "cio.h"
+#include "database.h"
+#include "describe.h"
#include "files.h"
+#include "initfile.h"
#include "invent.h"
#include "itemname.h"
#include "item_use.h"
@@ -34,6 +37,8 @@
#include "libutil.h"
#include "menu.h"
#include "message.h"
+#include "mon-pick.h"
+#include "mon-util.h"
#include "ouch.h"
#include "player.h"
#include "spl-cast.h"
@@ -70,6 +75,10 @@ static const char *features[] = {
"Lua user scripts",
#endif
+#ifdef WIZARD
+ "Wizard mode",
+#endif
+
#if defined(REGEX_POSIX)
"POSIX regexps",
#elif defined(REGEX_PCRE)
@@ -380,7 +389,8 @@ void list_armour()
{
estr << you.inv[armour_id].name(DESC_INVENTORY);
colour = menu_colour(estr.str(),
- menu_colour_item_prefix(you.inv[armour_id]));
+ menu_colour_item_prefix(you.inv[armour_id]),
+ "equip");
}
else if (!you_can_wear(i,true))
estr << " (unavailable)";
@@ -392,7 +402,7 @@ void list_armour()
estr << " none";
if (colour == MSGCOL_BLACK)
- colour = menu_colour(estr.str());
+ colour = menu_colour(estr.str(), "", "equip");
mpr( estr.str().c_str(), MSGCH_EQUIPMENT, colour);
}
@@ -421,7 +431,7 @@ void list_jewellery(void)
jstr << you.inv[jewellery_id].name(DESC_INVENTORY);
std::string
prefix = menu_colour_item_prefix(you.inv[jewellery_id]);
- colour = menu_colour(jstr.str(), prefix);
+ colour = menu_colour(jstr.str(), prefix, "equip");
}
else if (!you_tran_can_wear(i))
jstr << " (currently unavailable)";
@@ -429,7 +439,7 @@ void list_jewellery(void)
jstr << " none";
if (colour == MSGCOL_BLACK)
- colour = menu_colour(jstr.str());
+ colour = menu_colour(jstr.str(), "", "equip");
mpr( jstr.str().c_str(), MSGCH_EQUIPMENT, colour);
}
@@ -450,7 +460,8 @@ void list_weapons(void)
{
wstring += you.inv[weapon_id].name(DESC_INVENTORY_EQUIP);
colour = menu_colour(wstring,
- menu_colour_item_prefix(you.inv[weapon_id]));
+ menu_colour_item_prefix(you.inv[weapon_id]),
+ "equip");
}
else
{
@@ -460,7 +471,7 @@ void list_weapons(void)
wstring += " (currently unavailable)";
else
wstring += " empty hands";
- colour = menu_colour(wstring);
+ colour = menu_colour(wstring, "", "equip");
}
mpr(wstring.c_str(), MSGCH_EQUIPMENT, colour);
@@ -486,13 +497,14 @@ void list_weapons(void)
{
wstring += you.inv[i].name(DESC_INVENTORY_EQUIP);
colour = menu_colour(wstring,
- menu_colour_item_prefix(you.inv[i]));
+ menu_colour_item_prefix(you.inv[i]),
+ "equip");
}
else
wstring += " none";
if (colour == MSGCOL_BLACK)
- colour = menu_colour(wstring);
+ colour = menu_colour(wstring, "", "equip");
mpr(wstring.c_str(), MSGCH_EQUIPMENT, colour);
}
@@ -509,11 +521,12 @@ void list_weapons(void)
{
wstring += you.inv[item].name(DESC_INVENTORY_EQUIP);
colour = menu_colour(wstring,
- menu_colour_item_prefix(you.inv[item]));
+ menu_colour_item_prefix(you.inv[item]),
+ "equip");
}
if (colour == MSGCOL_BLACK)
- colour = menu_colour(wstring);
+ colour = menu_colour(wstring, "", "equip");
mpr( wstring.c_str(), MSGCH_EQUIPMENT, colour );
} // end list_weapons()
@@ -548,7 +561,8 @@ static const char *level_map_help =
"<w>Ctrl-X</w> : set travel eXclusion\n"
"<w>Ctrl-E</w> : Erase all travel exclusions\n"
"<w>Ctrl-W</w> : set Waypoint\n"
- "<w>Ctrl-C</w> : Clear level and main maps\n";
+ "<w>Ctrl-C</w> : Clear level and main maps\n"
+ "<w>Ctrl-F</w> : Forget level map\n";
static const char *targeting_help_1 =
"<h>Examine surroundings ('<w>x</w><h>' in main):\n"
@@ -679,6 +693,434 @@ help_file help_files[] = {
{ NULL, 0, false }
};
+static std::string list_commands_err = "";
+
+
+static bool compare_mon_names(MenuEntry *entry_a, MenuEntry* entry_b)
+{
+ monster_type *a = static_cast<monster_type*>( entry_a->data );
+ monster_type *b = static_cast<monster_type*>( entry_b->data );
+
+ if (*a == *b)
+ return false;
+
+ std::string a_name = mons_type_name(*a, DESC_PLAIN);
+ std::string b_name = mons_type_name(*b, DESC_PLAIN);
+ return (lowercase(a_name) < lowercase(b_name));
+}
+
+// Compare monsters by location-independant level, or by hitdice if
+// levels are equal, or by name if both level and hitdice are equal.
+static bool compare_mon_toughness(MenuEntry *entry_a, MenuEntry* entry_b)
+{
+ monster_type *a = static_cast<monster_type*>( entry_a->data );
+ monster_type *b = static_cast<monster_type*>( entry_b->data );
+
+ if (*a == *b)
+ return false;
+
+ int a_toughness = mons_global_level(*a);
+ int b_toughness = mons_global_level(*b);
+
+ if (a_toughness == b_toughness)
+ {
+ a_toughness = mons_type_hit_dice(*a);
+ b_toughness = mons_type_hit_dice(*b);
+ }
+
+ if (a_toughness == b_toughness)
+ {
+ std::string a_name = mons_type_name(*a, DESC_PLAIN);
+ std::string b_name = mons_type_name(*b, DESC_PLAIN);
+ return ( lowercase(a_name) < lowercase(b_name));
+ }
+
+ return (a_toughness < b_toughness);
+}
+
+class DescMenu : public Menu
+{
+public:
+ DescMenu( int _flags, bool _show_mon )
+ : Menu(_flags), sort_alpha(true), showing_monsters(_show_mon)
+ {
+ set_highlighter(NULL);
+
+ set_prompt();
+ }
+
+ bool sort_alpha;
+ bool showing_monsters;
+
+ void set_prompt()
+ {
+ std::string prompt = "Describe which? ";
+
+ if (showing_monsters)
+ {
+ if (sort_alpha)
+ prompt += "(CTRL-S to sort by monster toughness)";
+ else
+ prompt += "(CTRL-S to sort by name)";
+ }
+ set_title(new MenuEntry(prompt, MEL_TITLE));
+ }
+
+ void sort()
+ {
+ if (!showing_monsters)
+ return;
+
+ if (sort_alpha)
+ std::sort(items.begin(), items.end(), compare_mon_names);
+ else
+ std::sort(items.begin(), items.end(), compare_mon_toughness);
+
+ for (unsigned int i = 0, size = items.size(); i < size; i++)
+ {
+ const char letter = index_to_letter(i);
+
+ items[i]->hotkeys.clear();
+ items[i]->add_hotkey(letter);
+ }
+ }
+
+ void toggle_sorting()
+ {
+ if (!showing_monsters)
+ return;
+
+ sort_alpha = !sort_alpha;
+
+ sort();
+ set_prompt();
+ }
+};
+
+static std::vector<std::string> get_desc_keys(std::string regex,
+ db_find_filter filter)
+{
+ std::vector<std::string> key_matches = getLongDescKeysByRegex(regex,
+ filter);
+
+ if (key_matches.size() == 1)
+ return (key_matches);
+ else if (key_matches.size() > 52)
+ return (key_matches);
+
+ std::vector<std::string> body_matches = getLongDescBodiesByRegex(regex,
+ filter);
+
+ if (key_matches.size() == 0 && body_matches.size() == 0)
+ return (key_matches);
+ else if (key_matches.size() == 0 && body_matches.size() == 1)
+ return (body_matches);
+
+ // Merge key_matches and body_matches, discarding duplicates.
+ std::vector<std::string> tmp = key_matches;
+ tmp.insert(tmp.end(), body_matches.begin(), body_matches.end());
+ std::sort(tmp.begin(), tmp.end());
+ std::vector<std::string> all_matches;
+ for (unsigned int i = 0, size = tmp.size(); i < size; i++)
+ if (i == 0 || all_matches[all_matches.size() - 1] != tmp[i])
+ {
+ all_matches.push_back(tmp[i]);
+ }
+
+ return (all_matches);
+}
+
+static std::vector<std::string> get_monster_keys(unsigned char showchar)
+{
+ std::vector<std::string> mon_keys;
+
+ for (int i = 0; i < NUM_MONSTERS; i++)
+ {
+ if (i == MONS_PROGRAM_BUG || mons_global_level(i) == 0)
+ continue;
+
+ monsterentry *me = get_monster_data(i);
+
+ if (me == NULL || me->name == NULL || me->name[0] == '\0')
+ continue;
+
+ if (me->mc != i)
+ continue;
+
+ if (getLongDescription(me->name) == "")
+ continue;
+
+ if (me->showchar == showchar)
+ mon_keys.push_back(me->name);
+ }
+
+ return (mon_keys);
+}
+
+static bool monster_filter(std::string key, std::string body)
+{
+ int mon_num = get_monster_by_name(key.c_str(), true);
+ return (mon_num == MONS_PROGRAM_BUG || mons_global_level(mon_num) == 0);
+}
+
+static bool spell_filter(std::string key, std::string body)
+{
+ return (spell_by_name(key) == SPELL_NO_SPELL);
+}
+
+static bool feature_filter(std::string key, std::string body)
+{
+ return (spell_by_name(key) != SPELL_NO_SPELL
+ || get_monster_by_name(key.c_str(), true) != MONS_PROGRAM_BUG);
+}
+
+static bool do_description(std::string key)
+{
+ std::string desc = getLongDescription(key);
+
+ monster_type mon_num = get_monster_by_name(key, true);
+ if (mon_num != MONS_PROGRAM_BUG)
+ {
+ if (mons_genus(mon_num) == MONS_DRACONIAN)
+ {
+ monsters mon;
+
+ mon.type = mon_num;
+
+ switch (mon_num)
+ {
+ case MONS_BLACK_DRACONIAN:
+ case MONS_MOTTLED_DRACONIAN:
+ case MONS_YELLOW_DRACONIAN:
+ case MONS_GREEN_DRACONIAN:
+ case MONS_PURPLE_DRACONIAN:
+ case MONS_RED_DRACONIAN:
+ case MONS_WHITE_DRACONIAN:
+ case MONS_PALE_DRACONIAN:
+ mon.number = mon_num;
+ break;
+ default:
+ mon.number = 0;
+ break;
+ }
+
+ describe_monsters(mon);
+ return (false);
+ }
+
+ std::string symbol = "";
+ symbol += get_monster_data(mon_num)->showchar;
+ if (isupper(symbol[0]))
+ symbol = "cap-" + symbol;
+
+ std::string symbol_prefix = "__";
+ symbol_prefix += symbol;
+ symbol_prefix += "_prefix";
+ desc = getLongDescription(symbol_prefix) + desc;
+
+ std::string symbol_suffix = "__";
+ symbol_suffix += symbol;
+ symbol_suffix += "_suffix";
+ desc += getLongDescription(symbol_suffix);
+ }
+
+ key = uppercase_first(key);
+ key += "$$";
+
+ clrscr();
+ print_description(key + desc);
+
+ return (true);
+}
+
+static bool find_description()
+{
+ clrscr();
+ viewwindow(true, false);
+
+ mpr("Describe a (M)onster, (S)pell or (F)eature? ", MSGCH_PROMPT);
+
+ int ch = toupper(getch());
+ std::string type;
+ std::string extra;
+ db_find_filter filter;
+
+ switch(ch)
+ {
+ case 'M':
+ type = "monster";
+ extra = " Enter a single letter to list monsters displayed by "
+ "that symbol.";
+ filter = monster_filter;
+ break;
+ case 'S':
+ type = "spell";
+ filter = spell_filter;
+ break;
+ case 'F':
+ type = "feature";
+ filter = feature_filter;
+ break;
+ default:
+ list_commands_err = "Okay, then.";
+ return (false);
+ }
+
+ mprf(MSGCH_PROMPT,
+ "Describe a %s; partial names and regexps are fine.%s",
+ type.c_str(), extra.c_str());
+ mpr("Describe what? ", MSGCH_PROMPT);
+ char buf[80];
+ if (cancelable_get_line(buf, sizeof(buf)) || buf[0] == '\0')
+ {
+ list_commands_err = "Okay, then.";
+ return (false);
+ }
+
+ std::string regex = trimmed_string(buf);
+
+ if (regex == "")
+ {
+ list_commands_err = "Description must contain at least one non-space.";
+ return (false);
+ }
+
+ bool doing_mons = (ch == 'M');
+ bool by_mon_symbol = (doing_mons && regex.size() == 1);
+
+ // Try to get an exact match first.
+ if (!by_mon_symbol && !(*filter)(regex, ""))
+ {
+ // Try to get an exact match first.
+ std::string desc = getLongDescription(regex);
+
+ if (desc != "")
+ {
+ return do_description(regex);
+ }
+ }
+
+ std::vector<std::string> key_list;
+
+ if (by_mon_symbol)
+ key_list = get_monster_keys(regex[0]);
+ else
+ key_list = get_desc_keys(regex, filter);
+
+ if (key_list.size() == 0)
+ {
+ if (by_mon_symbol)
+ {
+ list_commands_err = "No monsters with symbol '";
+ list_commands_err += regex;
+ list_commands_err += "'";
+ }
+ else
+ {
+ list_commands_err = "No matching ";
+ list_commands_err += type;
+ list_commands_err += "s";
+ }
+ return (false);
+ }
+ else if (key_list.size() > 52)
+ {
+ if (by_mon_symbol)
+ {
+ list_commands_err = "Too many monsters with symbol '";
+ list_commands_err += regex;
+ list_commands_err += "' to display";
+ }
+ else
+ {
+ std::ostringstream os;
+ os << "Too many matching " << type << "s (" << key_list.size()
+ << ") to display.";
+ list_commands_err = os.str();
+ }
+ return (false);
+ }
+ else if (key_list.size() == 1)
+ {
+ return do_description(key_list[0]);
+ }
+
+ std::sort(key_list.begin(), key_list.end());
+
+ DescMenu desc_menu(MF_SINGLESELECT | MF_ANYPRINTABLE |
+ MF_ALWAYS_SHOW_MORE | MF_ALLOW_FORMATTING,
+ doing_mons);
+ desc_menu.set_tag("description");
+ std::list<monster_type> monster_types;
+ for (unsigned int i = 0, size = key_list.size(); i < size; i++)
+ {
+ const char letter = index_to_letter(i);
+ std::string str = uppercase_first(key_list[i]);
+
+ MenuEntry *me = new MenuEntry(uppercase_first(key_list[i]),
+ MEL_ITEM, 1, letter);
+
+ if (doing_mons)
+ {
+ monster_type mon = get_monster_by_name(str, true);
+ unsigned char colour = mons_class_colour(mon);
+
+ monster_types.push_back(mon);
+
+ if (colour == BLACK)
+ colour = LIGHTGREY;
+
+ std::string prefix = "(<";
+ prefix += colour_to_str(colour);
+ prefix += ">";
+ prefix += stringize_glyph(mons_char(mon));
+ prefix += "</";
+ prefix += colour_to_str(colour);
+ prefix += ">) ";
+
+ me->text = prefix + str;
+ me->data = &*monster_types.rbegin();
+ }
+ else
+ me->data = (void*) &key_list[i];
+
+ desc_menu.add_entry(me);
+ }
+
+ desc_menu.sort();
+
+ while (true)
+ {
+ std::vector<MenuEntry*> sel = desc_menu.show();
+ redraw_screen();
+ if ( sel.empty() )
+ {
+ if (doing_mons && desc_menu.getkey() == CONTROL('S'))
+ desc_menu.toggle_sorting();
+ else
+ return (false);
+ }
+ else
+ {
+ ASSERT(sel.size() == 1);
+ ASSERT(sel[0]->hotkeys.size() == 1);
+
+ std::string key;
+
+ if (doing_mons)
+ key = mons_type_name(*(monster_type*) sel[0]->data, DESC_PLAIN);
+ else
+ key = *((std::string*) sel[0]->data);
+
+ if (do_description(key))
+ if ( getch() == 0 )
+ getch();
+ }
+ }
+
+ return (false);
+}
+
static int keyhelp_keyfilter(int ch)
{
switch (ch)
@@ -690,10 +1132,17 @@ static int keyhelp_keyfilter(int ch)
display_notes();
return -1;
}
- // fall through
- default:
- return ch;
+ break;
+
+ case '/':
+ if (find_description())
+ if ( getch() == 0 )
+ getch();
+
+ viewwindow(true, false);
+ return -1;
}
+ return ch;
}
static void show_keyhelp_menu(const std::vector<formatted_string> &lines,
@@ -707,6 +1156,7 @@ static void show_keyhelp_menu(const std::vector<formatted_string> &lines,
if (easy_exit)
flags |= MF_EASY_EXIT;
cmd_help.set_flags(flags, false);
+ cmd_help.set_tag("help");
// FIXME: Allow for hiding Page down when at the end of the listing, ditto
// for page up at start of listing.
@@ -733,6 +1183,7 @@ static void show_keyhelp_menu(const std::vector<formatted_string> &lines,
"<w>~</w>: Macros help\n"
"<w>!</w>: Options help\n"
"<w>%</w>: Table of aptitudes\n"
+ "<w>/</w>: Lookup description\n"
"<w>Home</w>: This screen\n",
true, true, cmdhelp_textfilter);
@@ -842,11 +1293,15 @@ void show_interlevel_travel_depth_help()
show_specific_help( interlevel_travel_depth_help );
}
-void list_commands(bool wizard, int hotkey)
+void list_commands(bool wizard, int hotkey, bool do_redraw_screen)
{
if (wizard)
{
list_wizard_commands();
+
+ if (do_redraw_screen)
+ redraw_screen();
+
return;
}
@@ -1007,6 +1462,8 @@ void list_commands(bool wizard, int hotkey)
"<w>p</w> : Pray\n"
"<w>Z</w> : cast a spell\n"
"<w>!</w> : shout or command allies\n"
+ "<w>`</w> : re-do previous command\n"
+ "<w>0</w> : repeat next command # of times\n"
" \n",
true, true, cmdhelp_textfilter);
@@ -1017,6 +1474,7 @@ void list_commands(bool wizard, int hotkey)
"<w>Ctrl-P</w> : show Previous messages\n"
"<w>Ctrl-R</w> : Redraw screen\n"
"<w>Ctrl-C</w> : Clear main and level maps\n"
+ "<w>Ctrl-I</w> : annotate the dungeon level\n"
"<w>#</w> : dump character to file\n"
"<w>:</w> : add note (use <w>?:</w> to read notes)\n"
"<w>~</w> : add macro\n"
@@ -1056,7 +1514,17 @@ void list_commands(bool wizard, int hotkey)
"stashes, and <w>Ctrl-E</w> to erase them.\n",
true, true, cmdhelp_textfilter);
+ list_commands_err = "";
show_keyhelp_menu(cols.formatted_lines(), true, false, hotkey);
+
+ if (do_redraw_screen)
+ {
+ clrscr();
+ redraw_screen();
+ }
+
+ if (list_commands_err != "")
+ mpr(list_commands_err.c_str());
}
void list_tutorial_help()
@@ -1145,9 +1613,11 @@ static void list_wizard_commands()
cols.add_formatted(0,
"a : acquirement\n"
"A : set all skills to level\n"
+ "Ctrl-A : generate new Abyss area\n"
"b : controlled blink\n"
"B : banish yourself to the Abyss\n"
"c : card effect\n"
+ "C : (un)curse item\n"
"g : add a skill\n"
"G : banish all monsters\n"
"Ctrl-G : save ghost (bones file)\n"
@@ -1165,11 +1635,11 @@ static void list_wizard_commands()
"r : change character's species\n"
"s : gain 20000 skill points\n"
"S : set skill to level\n"
- "t : tweak object properties\n"
- "T : make a trap\n",
+ "t : tweak object properties\n",
true, true);
cols.add_formatted(1,
+ "T : make a trap\n"
"v : show gold value of an item\n"
"x : gain an experience level\n"
"Ctrl-X : change experience level\n"