diff options
-rw-r--r-- | crawl-ref/source/command.cc | 437 | ||||
-rw-r--r-- | crawl-ref/source/database.cc | 38 | ||||
-rw-r--r-- | crawl-ref/source/database.h | 14 | ||||
-rw-r--r-- | crawl-ref/source/debug.cc | 32 | ||||
-rw-r--r-- | crawl-ref/source/spl-util.cc | 24 | ||||
-rw-r--r-- | crawl-ref/source/spl-util.h | 3 |
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 ®ex, - 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 ®ex, - 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 ®ex) +std::vector<std::string> getLongDescKeysByRegex(const std::string ®ex, + db_find_filter filter) { if (!descriptionDB) { @@ -470,10 +469,11 @@ std::vector<std::string> getLongDescKeysByRegex(const std::string ®ex) 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 ®ex) +std::vector<std::string> getLongDescBodiesByRegex(const std::string ®ex, + db_find_filter filter) { if (!descriptionDB) { @@ -481,7 +481,7 @@ std::vector<std::string> 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<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 ®ex, - 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 ®ex, - 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 ®ex); -std::vector<std::string> getLongDescBodiesByRegex(const std::string ®ex); +std::vector<std::string> getLongDescKeysByRegex(const std::string ®ex, + db_find_filter filter = NULL); +std::vector<std::string> 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<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 ); |