summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-09-24 05:04:58 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-09-24 05:04:58 +0000
commitee8bfe95919cb737826f04629698df9258200020 (patch)
treeb8b4f48ee90c6c6c3efade2c8793fdd0f249f230 /crawl-ref
parentbcbd0cca53601270c2b128eb9c89a9185cb40336 (diff)
downloadcrawl-ref-ee8bfe95919cb737826f04629698df9258200020.tar.gz
crawl-ref-ee8bfe95919cb737826f04629698df9258200020.zip
?/ now asks if you want to describe a monster, spell, or feature, and
filters out matches if they aren't of the desired type. If there's more than one match, after selecting a match to look at, exiting from the description will return you to the menu, rather than to the dungeon. If you've asked for monsters, you can toggle sorting of the menu between alphabetical and by aproximated monster toughness (this probably still needs some work, since it seems to say that ordinary worms are tougher than brain worms). Only the monster symbol is coloured when showing a menu of monsters to describe. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2185 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-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 );