From ee8bfe95919cb737826f04629698df9258200020 Mon Sep 17 00:00:00 2001 From: zelgadis Date: Mon, 24 Sep 2007 05:04:58 +0000 Subject: ?/ 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 --- crawl-ref/source/command.cc | 437 ++++++++++++++++++++++++++++++------------- crawl-ref/source/database.cc | 38 ++-- crawl-ref/source/database.h | 14 +- crawl-ref/source/debug.cc | 32 ++-- crawl-ref/source/spl-util.cc | 24 +++ crawl-ref/source/spl-util.h | 3 + 6 files changed, 379 insertions(+), 169 deletions(-) (limited to 'crawl-ref') 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 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 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 get_desc_keys(std::string regex, + db_find_filter filter) { - std::vector key_matches = getLongDescKeysByRegex(regex); + std::vector 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 body_matches = getLongDescBodiesByRegex(regex); + std::vector 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 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 get_monster_keys(unsigned char showchar) { std::vector 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 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 += ") "; + + 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 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 database_find_keys(DBM *database, const std::string ®ex, - bool ignore_case) + bool ignore_case, + db_find_filter filter) { text_pattern tpat(regex, ignore_case); std::vector matches; @@ -165,14 +166,12 @@ std::vector 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 database_find_keys(DBM *database, std::vector database_find_bodies(DBM *database, const std::string ®ex, - bool ignore_case) + bool ignore_case, + db_find_filter filter) { text_pattern tpat(regex, ignore_case); std::vector matches; @@ -193,17 +193,15 @@ std::vector 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 getLongDescKeysByRegex(const std::string ®ex) +std::vector getLongDescKeysByRegex(const std::string ®ex, + db_find_filter filter) { if (!descriptionDB) { @@ -470,10 +469,11 @@ std::vector getLongDescKeysByRegex(const std::string ®ex) return (empty); } - return database_find_keys(descriptionDB, regex, true); + return database_find_keys(descriptionDB, regex, true, filter); } -std::vector getLongDescBodiesByRegex(const std::string ®ex) +std::vector getLongDescBodiesByRegex(const std::string ®ex, + db_find_filter filter) { if (!descriptionDB) { @@ -481,7 +481,7 @@ std::vector getLongDescBodiesByRegex(const std::string ®ex) return (empty); } - return database_find_bodies(descriptionDB, regex, true); + return database_find_bodies(descriptionDB, regex, true, filter); } static std::vector 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 database_find_keys(DBM *database, const std::string ®ex, - bool ignore_case = false); + bool ignore_case = false, + db_find_filter filter = NULL); std::vector database_find_bodies(DBM *database, const std::string ®ex, - bool ignore_case = false); + bool ignore_case = false, + db_find_filter filter = NULL); std::string getLongDescription(const std::string &key); -std::vector getLongDescKeysByRegex(const std::string ®ex); -std::vector getLongDescBodiesByRegex(const std::string ®ex); +std::vector getLongDescKeysByRegex(const std::string ®ex, + db_find_filter filter = NULL); +std::vector getLongDescBodiesByRegex(const std::string ®ex, + 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(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(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(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 ); -- cgit v1.2.3-54-g00ecf