summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/command.cc437
-rw-r--r--crawl-ref/source/database.cc38
-rw-r--r--crawl-ref/source/database.h14
-rw-r--r--crawl-ref/source/debug.cc32
-rw-r--r--crawl-ref/source/spl-util.cc24
-rw-r--r--crawl-ref/source/spl-util.h3
6 files changed, 379 insertions, 169 deletions
diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc
index e8568a1079..fba34c036e 100644
--- a/crawl-ref/source/command.cc
+++ b/crawl-ref/source/command.cc
@@ -29,6 +29,7 @@
#include "database.h"
#include "describe.h"
#include "files.h"
+#include "initfile.h"
#include "invent.h"
#include "itemname.h"
#include "item_use.h"
@@ -36,6 +37,7 @@
#include "libutil.h"
#include "menu.h"
#include "message.h"
+#include "mon-pick.h"
#include "mon-util.h"
#include "ouch.h"
#include "player.h"
@@ -684,81 +686,129 @@ help_file help_files[] = {
static std::string list_commands_err = "";
-static std::string select_desc_key(std::vector<std::string> keys)
+
+static bool compare_mon_names(MenuEntry *entry_a, MenuEntry* entry_b)
+{
+ int a = (int) entry_a->data;
+ int b = (int) 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 level if there's a place where they both have a
+// level defined, or by hitdice otherwise. If monsters are of equal
+// toughness, compare names.
+static bool compare_mon_toughness(MenuEntry *entry_a, MenuEntry* entry_b)
{
- Menu desc_menu(MF_SINGLESELECT | MF_ANYPRINTABLE |
- MF_ALWAYS_SHOW_MORE | MF_ALLOW_FORMATTING);
+ int a = (int) entry_a->data;
+ int b = (int) entry_b->data;
- desc_menu.set_title(new MenuEntry("Describe which?", MEL_TITLE));
+ if (a == b)
+ return false;
- desc_menu.set_highlighter(NULL);
+ int a_toughness = (*mons_standard_level)(a);
+ int b_toughness = (*mons_standard_level)(b);
- for (unsigned int i = 0, size = keys.size(); i < size; i++)
+ if (a_toughness < 1 || a_toughness >= 99
+ || b_toughness < 1 || b_toughness >= 99
+ || a_toughness == b_toughness)
{
- const char letter = index_to_letter(i);
- std::string str = uppercase_first(keys[i]);
+ a_toughness = mons_type_hit_dice(a);
+ b_toughness = mons_type_hit_dice(b);
+ }
- MenuEntry *me = new MenuEntry(uppercase_first(keys[i]),
- MEL_ITEM, 1, letter);
+ 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);
+}
- monster_type mon = get_monster_by_name(str, true);
- if (mon < NUM_MONSTERS && mon != MONS_PROGRAM_BUG)
+class DescMenu : public Menu
+{
+public:
+ DescMenu( int _flags, bool _show_mon )
+ : Menu(_flags), sort_alpha(true), showing_monsters(_show_mon)
{
- monsterentry *mon_en = get_monster_data(mon);
+ set_highlighter(NULL);
+
+ set_prompt();
+ }
+
+ bool sort_alpha;
+ bool showing_monsters;
- me->colour = mon_en->colour;
- if (me->colour == BLACK)
- me->colour = LIGHTGREY;
- str += " ('";
- str += (char) mon_en->showchar;
- str += "')";
+ void set_prompt()
+ {
+ std::string prompt = "Describe which? ";
- me->text = str;
+ 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));
}
- me->data = (void*) &keys[i];
+ void sort()
+ {
+ if (!showing_monsters)
+ return;
- desc_menu.add_entry(me);
- }
+ if (sort_alpha)
+ std::sort(items.begin(), items.end(), compare_mon_names);
+ else
+ std::sort(items.begin(), items.end(), compare_mon_toughness);
- std::vector<MenuEntry*> sel = desc_menu.show();
- redraw_screen();
- if ( sel.empty() )
- {
- list_commands_err = "Okay, then.";
- return ("");
- }
- else
- {
- ASSERT(sel.size() == 1);
- ASSERT(sel[0]->hotkeys.size() == 1);
- return *((std::string*) sel[0]->data);
- }
-}
+ 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);
+ }
+ }
-static std::string find_description_key(std::string regex)
+ 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);
+ std::vector<std::string> key_matches = getLongDescKeysByRegex(regex,
+ filter);
if (key_matches.size() == 1)
- return (key_matches[0]);
+ return (key_matches);
else if (key_matches.size() > 52)
- {
- list_commands_err = "Too many matches for '";
- list_commands_err += regex + "'";
- return ("");
- }
+ return (key_matches);
- std::vector<std::string> body_matches = getLongDescBodiesByRegex(regex);
+ std::vector<std::string> body_matches = getLongDescBodiesByRegex(regex,
+ filter);
if (key_matches.size() == 0 && body_matches.size() == 0)
- {
- list_commands_err = "No matches for '";
- list_commands_err += regex + "'";
- return ("");
- }
+ return (key_matches);
else if (key_matches.size() == 0 && body_matches.size() == 1)
- return (body_matches[0]);
+ return (body_matches);
// Merge key_matches and body_matches, discarding duplicates.
std::vector<std::string> tmp = key_matches;
@@ -771,17 +821,10 @@ static std::string find_description_key(std::string regex)
all_matches.push_back(tmp[i]);
}
- if (all_matches.size() > 52)
- {
- list_commands_err = "Too many matches for '";
- list_commands_err += regex + "'";
- return ("");
- }
-
- return select_desc_key(all_matches);
+ return (all_matches);
}
-static std::string find_monster_key(unsigned char showchar)
+static std::vector<std::string> get_monster_keys(unsigned char showchar)
{
std::vector<std::string> mon_keys;
@@ -805,82 +848,31 @@ static std::string find_monster_key(unsigned char showchar)
mon_keys.push_back(me->name);
}
- if (mon_keys.size() == 0)
- {
- list_commands_err = "No monsters displayed with symbol '";
- list_commands_err += showchar;
- list_commands_err += "'";
-
- return ("");
- }
- else if (mon_keys.size() > 52)
- {
- list_commands_err = "Too many monsters for symbol '";
- list_commands_err += showchar;
- list_commands_err += "'";
-
- return ("");
- }
-
- std::sort(mon_keys.begin(), mon_keys.end());
-
- return select_desc_key(mon_keys);
+ return (mon_keys);
}
-static bool find_description()
+static bool monster_filter(std::string key, std::string body)
{
- clrscr();
- viewwindow(true, false);
-
- mpr("Describe a monster, spell, or feature; regexs and partial names "
- "are fine. Enter a single letter to list monsters displayed by "
- "that symbol.");
- mpr("Describe what? ");
- 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);
- }
-
- // Try to get an exact match first.
- std::string key;
- std::string desc;
-
- if (regex.size() == 1)
- {
- key = find_monster_key(regex[0]);
-
- if (key == "")
- return (false);
-
- desc = getLongDescription(key);
- }
- else if (desc == "")
- {
- // Try to get an exact match first.
- key = regex;
- desc = getLongDescription(key);
-
+ return (get_monster_by_name(key.c_str(), true) == MONS_PROGRAM_BUG);
+}
- key = find_description_key(regex);
+static bool spell_filter(std::string key, std::string body)
+{
+ return (spell_by_name(key) == SPELL_NO_SPELL);
+}
- if (key == "")
- return (false);
+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);
+}
- desc = getLongDescription(key);
- }
+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 < NUM_MONSTERS && mon_num != MONS_PROGRAM_BUG)
+ if (mon_num != MONS_PROGRAM_BUG)
{
if (mons_genus(mon_num) == MONS_DRACONIAN)
{
@@ -934,6 +926,193 @@ static bool find_description()
return (true);
}
+static bool find_description()
+{
+ clrscr();
+ viewwindow(true, false);
+
+ mpr("Describe a (M)onster, (S)pell or (F)eature? ");
+
+ 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("Describe a %s; partial names and regexs are fine.%s",
+ type.c_str(), extra.c_str());
+ mpr("Describe what? ");
+ 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
+ {
+ char num_buf[10];
+ sprintf(num_buf, "%d", key_list.size());
+ list_commands_err = "Too many matching ";
+ list_commands_err += type;
+ list_commands_err += "s (";
+ list_commands_err += num_buf;
+ list_commands_err += ") to display";
+ }
+ 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);
+
+ 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);
+
+ if (colour == BLACK)
+ colour = LIGHTGREY;
+
+ std::string prefix = "(<";
+ prefix += colour_to_str(colour);
+ prefix += ">";
+ prefix += (char) mons_char(mon);
+ prefix += "</";
+ prefix += colour_to_str(colour);
+ prefix += ">) ";
+
+ me->text = prefix + str;
+ me->data = (void*) mon;
+ }
+ 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((int) 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)
diff --git a/crawl-ref/source/database.cc b/crawl-ref/source/database.cc
index 86ae4b098c..0e9b598f9c 100644
--- a/crawl-ref/source/database.cc
+++ b/crawl-ref/source/database.cc
@@ -154,7 +154,8 @@ datum database_fetch(DBM *database, const std::string &key)
std::vector<std::string> database_find_keys(DBM *database,
const std::string &regex,
- bool ignore_case)
+ bool ignore_case,
+ db_find_filter filter)
{
text_pattern tpat(regex, ignore_case);
std::vector<std::string> matches;
@@ -165,14 +166,12 @@ std::vector<std::string> database_find_keys(DBM *database,
{
std::string key((const char *)dbKey.dptr, dbKey.dsize);
- if (key.find("__") != std::string::npos)
+ if (tpat.matches(key) &&
+ key.find("__") == std::string::npos
+ && (filter == NULL || !(*filter)(key, "")))
{
- dbKey = dbm_nextkey(database);
- continue;
- }
-
- if (tpat.matches(key))
matches.push_back(key);
+ }
dbKey = dbm_nextkey(database);
}
@@ -182,7 +181,8 @@ std::vector<std::string> database_find_keys(DBM *database,
std::vector<std::string> database_find_bodies(DBM *database,
const std::string &regex,
- bool ignore_case)
+ bool ignore_case,
+ db_find_filter filter)
{
text_pattern tpat(regex, ignore_case);
std::vector<std::string> matches;
@@ -193,17 +193,15 @@ std::vector<std::string> database_find_bodies(DBM *database,
{
std::string key((const char *)dbKey.dptr, dbKey.dsize);
- if (key.find("__") != std::string::npos)
- {
- dbKey = dbm_nextkey(database);
- continue;
- }
-
datum dbBody = dbm_fetch(database, dbKey);
std::string body((const char *)dbBody.dptr, dbBody.dsize);
- if (tpat.matches(body))
+ if (tpat.matches(body) &&
+ key.find("__") == std::string::npos
+ && (filter == NULL || !(*filter)(key, body)))
+ {
matches.push_back(key);
+ }
dbKey = dbm_nextkey(database);
}
@@ -462,7 +460,8 @@ std::string getLongDescription(const std::string &key)
return std::string((const char *)result.dptr, result.dsize);
}
-std::vector<std::string> getLongDescKeysByRegex(const std::string &regex)
+std::vector<std::string> getLongDescKeysByRegex(const std::string &regex,
+ db_find_filter filter)
{
if (!descriptionDB)
{
@@ -470,10 +469,11 @@ std::vector<std::string> getLongDescKeysByRegex(const std::string &regex)
return (empty);
}
- return database_find_keys(descriptionDB, regex, true);
+ return database_find_keys(descriptionDB, regex, true, filter);
}
-std::vector<std::string> getLongDescBodiesByRegex(const std::string &regex)
+std::vector<std::string> getLongDescBodiesByRegex(const std::string &regex,
+ db_find_filter filter)
{
if (!descriptionDB)
{
@@ -481,7 +481,7 @@ std::vector<std::string> getLongDescBodiesByRegex(const std::string &regex)
return (empty);
}
- return database_find_bodies(descriptionDB, regex, true);
+ return database_find_bodies(descriptionDB, regex, true, filter);
}
static std::vector<std::string> description_txt_paths()
diff --git a/crawl-ref/source/database.h b/crawl-ref/source/database.h
index 357ab62a9a..09b90364ea 100644
--- a/crawl-ref/source/database.h
+++ b/crawl-ref/source/database.h
@@ -40,16 +40,22 @@ void databaseSystemShutdown();
DBM *openDB(const char *dbFilename);
datum database_fetch(DBM *database, const std::string &key);
+typedef bool (*db_find_filter)(std::string key, std::string body);
+
std::vector<std::string> database_find_keys(DBM *database,
const std::string &regex,
- bool ignore_case = false);
+ bool ignore_case = false,
+ db_find_filter filter = NULL);
std::vector<std::string> database_find_bodies(DBM *database,
const std::string &regex,
- bool ignore_case = false);
+ bool ignore_case = false,
+ db_find_filter filter = NULL);
std::string getLongDescription(const std::string &key);
-std::vector<std::string> getLongDescKeysByRegex(const std::string &regex);
-std::vector<std::string> getLongDescBodiesByRegex(const std::string &regex);
+std::vector<std::string> getLongDescKeysByRegex(const std::string &regex,
+ db_find_filter filter = NULL);
+std::vector<std::string> getLongDescBodiesByRegex(const std::string &regex,
+ db_find_filter filter = NULL);
std::string getShoutString(const std::string &monst,
const std::string &suffix = "");
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index c06a9533c8..2a953f7782 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -347,30 +347,28 @@ void cast_spec_spell(void)
void cast_spec_spell_name(void)
{
char specs[80];
- char spname[80];
mpr( "Cast which spell by name? ", MSGCH_PROMPT );
get_input_line( specs, sizeof( specs ) );
-
- for (int i = 0; i < NUM_SPELLS; i++)
+
+ if (specs[0] == '\0')
{
- strncpy( spname,
- spell_title(static_cast<spell_type>(i)),
- sizeof( spname ) );
+ canned_msg( MSG_OK );
+ crawl_state.cancel_cmd_repeat();
+ return;
+ }
- if (strstr( strlwr(spname), strlwr(specs) ) != NULL)
- {
- if (your_spells(static_cast<spell_type>(i), 0, false)
- == SPRET_ABORT)
- {
- crawl_state.cancel_cmd_repeat();
- }
- return;
- }
+ spell_type type = spell_by_name(specs);
+ if (type == SPELL_NO_SPELL)
+ {
+ mpr((one_chance_in(20)) ? "Maybe you should go back to WIZARD school."
+ : "I couldn't find that spell.");
+ crawl_state.cancel_cmd_repeat();
+ return;
}
- mpr((one_chance_in(20)) ? "Maybe you should go back to WIZARD school."
- : "I couldn't find that spell.");
+ if (your_spells(type, 0, false) == SPRET_ABORT)
+ crawl_state.cancel_cmd_repeat();
}
#endif
diff --git a/crawl-ref/source/spl-util.cc b/crawl-ref/source/spl-util.cc
index d1b606199b..eb0865091b 100644
--- a/crawl-ref/source/spl-util.cc
+++ b/crawl-ref/source/spl-util.cc
@@ -83,6 +83,30 @@ void init_spell_descs(void)
return;
} // end init_spell_descs()
+spell_type spell_by_name(const char* name)
+{
+ if (name == NULL || strlen(name) == 0)
+ return (SPELL_NO_SPELL);
+
+ char spname[80];
+
+ for (int i = 0; i < NUM_SPELLS; i++)
+ {
+ spell_type type = static_cast<spell_type>(i);
+ strncpy( spname, spell_title(type), sizeof( spname ) );
+
+ if (strcasecmp(spname, name) == 0)
+ return (type);
+ }
+
+ return (SPELL_NO_SPELL);
+}
+
+spell_type spell_by_name(std::string name)
+{
+ return spell_by_name(name.c_str());
+}
+
int get_spell_slot_by_letter( char letter )
{
ASSERT( isalpha( letter ) );
diff --git a/crawl-ref/source/spl-util.h b/crawl-ref/source/spl-util.h
index 4570ea017a..4e86d9ff3e 100644
--- a/crawl-ref/source/spl-util.h
+++ b/crawl-ref/source/spl-util.h
@@ -64,6 +64,9 @@ struct spell_desc
//* * called from: acr
void init_spell_descs(void);
+spell_type spell_by_name(const char* name);
+spell_type spell_by_name(std::string name);
+
int get_spell_slot_by_letter( char letter );
spell_type get_spell_by_letter( char letter );