summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-09-22 02:48:12 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2007-09-22 02:48:12 +0000
commit94c42a1d74076a4767ed7d3c1394adb813e817d7 (patch)
treeec513b612f05f80d4c313430074b7dcc91ab3361 /crawl-ref
parenta0fc101bb8397b81ccd9cf5c1137764e14c57e89 (diff)
downloadcrawl-ref-94c42a1d74076a4767ed7d3c1394adb813e817d7.tar.gz
crawl-ref-94c42a1d74076a4767ed7d3c1394adb813e817d7.zip
If there are multiple matches to the "?/" describe command, then
they will be displayed in a menu, which the user can select from. Menu entries for monsters will be given the same color as the monster, with the monster's symbol added to the end of the string. Entering a single character as input to the "?/" describe command lists all monsters with that display symbol. Monsters sharing the same display symbol can now have the same string appended or prepended to their description (implemented to make the "?/" command have to deal with less special cases). Currently only used by nagas with __N_suffix. Has the side-effect advantage that the naga subspicies description and the "attractive/repulsive" description goes before the big block of quoted text, rather than after it. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2177 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/acr.cc9
-rw-r--r--crawl-ref/source/command.cc273
-rw-r--r--crawl-ref/source/command.h3
-rw-r--r--crawl-ref/source/dat/descript/monsters.txt74
-rw-r--r--crawl-ref/source/database.cc50
-rw-r--r--crawl-ref/source/database.h6
-rw-r--r--crawl-ref/source/describe.cc39
7 files changed, 370 insertions, 84 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc
index 77d6fa8ed3..392cf51b65 100644
--- a/crawl-ref/source/acr.cc
+++ b/crawl-ref/source/acr.cc
@@ -438,8 +438,7 @@ static void handle_wizard_command( void )
switch (wiz_command)
{
case '?':
- list_commands(true); // tell it to list wizard commands
- redraw_screen();
+ list_commands(true, 0, true); // tell it to list wizard commands
break;
case CONTROL('G'):
@@ -1963,10 +1962,12 @@ void process_command( command_type cmd )
case CMD_DISPLAY_COMMANDS:
if (Options.tutorial_left)
+ {
list_tutorial_help();
+ redraw_screen();
+ }
else
- list_commands(false);
- redraw_screen();
+ list_commands(false, 0, true);
break;
case CMD_EXPERIENCE_CHECK:
diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc
index 26f55a4f0e..bb90b0423c 100644
--- a/crawl-ref/source/command.cc
+++ b/crawl-ref/source/command.cc
@@ -36,6 +36,7 @@
#include "libutil.h"
#include "menu.h"
#include "message.h"
+#include "mon-util.h"
#include "ouch.h"
#include "player.h"
#include "spl-cast.h"
@@ -681,63 +682,253 @@ help_file help_files[] = {
{ NULL, 0, false }
};
-static void find_description()
+static std::string list_commands_err = "";
+
+static std::string select_desc_key(std::vector<std::string> keys)
+{
+ Menu desc_menu(MF_SINGLESELECT | MF_ANYPRINTABLE |
+ MF_ALWAYS_SHOW_MORE | MF_ALLOW_FORMATTING);
+
+ desc_menu.set_title(new MenuEntry("Describe which?", MEL_TITLE));
+
+ desc_menu.set_highlighter(NULL);
+
+ for (unsigned int i = 0, size = keys.size(); i < size; i++)
+ {
+ const char letter = index_to_letter(i);
+ std::string str = uppercase_first(keys[i]);
+
+ MenuEntry *me = new MenuEntry(uppercase_first(keys[i]),
+ MEL_ITEM, 1, letter);
+
+ monster_type mon = get_monster_by_name(str, true);
+ if (mon < NUM_MONSTERS && mon != MONS_PROGRAM_BUG)
+ {
+ monsterentry *mon_en = get_monster_data(mon);
+
+ me->colour = mon_en->colour;
+ if (me->colour == BLACK)
+ me->colour = LIGHTGREY;
+ str += " ('";
+ str += (char) mon_en->showchar;
+ str += "')";
+
+ me->text = str;
+ }
+
+ me->data = (void*) &keys[i];
+
+ desc_menu.add_entry(me);
+ }
+
+ 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);
+ }
+}
+
+static std::string find_description_key(std::string regex)
+{
+ std::vector<std::string> key_matches = getLongDescKeysByRegex(regex);
+
+ if (key_matches.size() == 1)
+ return (key_matches[0]);
+ else if (key_matches.size() > 52)
+ {
+ list_commands_err = "Too many matches for '";
+ list_commands_err += regex + "'";
+ return ("");
+ }
+
+ std::vector<std::string> body_matches = getLongDescBodiesByRegex(regex);
+
+ if (key_matches.size() == 0 && body_matches.size() == 0)
+ {
+ list_commands_err = "No matches for '";
+ list_commands_err += regex + "'";
+ return ("");
+ }
+ else if (key_matches.size() == 0 && body_matches.size() == 1)
+ return (body_matches[0]);
+
+ // 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]);
+ }
+
+ if (all_matches.size() > 52)
+ {
+ list_commands_err = "Too many matches for '";
+ list_commands_err += regex + "'";
+ return ("");
+ }
+
+ return select_desc_key(all_matches);
+}
+
+static std::string find_monster_key(unsigned char showchar)
+{
+ std::vector<std::string> mon_keys;
+
+ for (int i = 0; i < NUM_MONSTERS; i++)
+ {
+ if (i == MONS_PROGRAM_BUG)
+ 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);
+ }
+
+ 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);
+}
+
+static bool find_description()
{
clrscr();
viewwindow(true, false);
- mpr("Describe what? (can be a partial name or a regex) ");
+ 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')
{
- canned_msg( MSG_OK);
- return;
+ list_commands_err = "Okay, then.";
+ return (false);
}
std::string regex = trimmed_string(buf);
if (regex == "")
{
- mpr("Description must contain at least one non-space.");
- return;
+ list_commands_err = "Description must contain at least one non-space.";
+ return (false);
}
// Try to get an exact match first.
- std::string key = regex;
- std::string desc = getLongDescription(key);
+ std::string key;
+ std::string desc;
- if (desc == "")
+ if (regex.size() == 1)
{
- std::vector<std::string> matches = getLongDescriptionByRegex(regex);
+ key = find_monster_key(regex[0]);
- if (matches.size() == 0)
- {
- mprf("Nothing matches '%s'", buf);
- return;
- }
- else if (matches.size() > 1)
- {
- std::string prefix = "No exact match for '" +
- regex + "', possible matches are: ";
+ if (key == "")
+ return (false);
- // Use mpr_comma_separated_list() because the list
- // might be *LONG*.
- mpr_comma_separated_list(prefix, matches, " and ", ", ",
- MSGCH_PLAIN);
- return;
- }
- else
+ desc = getLongDescription(key);
+ }
+ else if (desc == "")
+ {
+ // Try to get an exact match first.
+ key = regex;
+ desc = getLongDescription(key);
+
+
+ key = find_description_key(regex);
+
+ if (key == "")
+ return (false);
+
+ desc = getLongDescription(key);
+ }
+
+ monster_type mon_num = get_monster_by_name(key, true);
+ if (mon_num < NUM_MONSTERS && mon_num != MONS_PROGRAM_BUG)
+ {
+ if (mons_genus(mon_num) == MONS_DRACONIAN)
{
- // Only one match, use that.
- key = matches[0];
- desc = getLongDescription(key);
+ 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);
}
+
+ unsigned char symbol = get_monster_data(mon_num)->showchar;
+
+ 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 int keyhelp_keyfilter(int ch)
@@ -754,13 +945,11 @@ static int keyhelp_keyfilter(int ch)
break;
case '/':
- find_description();
-
- if ( getch() == 0 )
- getch();
+ if (find_description())
+ if ( getch() == 0 )
+ getch();
viewwindow(true, false);
-
return -1;
}
return ch;
@@ -913,11 +1102,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;
}
@@ -1129,7 +1322,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()
diff --git a/crawl-ref/source/command.h b/crawl-ref/source/command.h
index 174acce2f7..b64e4059d0 100644
--- a/crawl-ref/source/command.h
+++ b/crawl-ref/source/command.h
@@ -62,7 +62,8 @@ void show_levelmap_help();
void show_targeting_help();
void show_interlevel_travel_branch_help();
void show_interlevel_travel_depth_help();
-void list_commands(bool wizard, int hotkey = 0);
+void list_commands(bool wizard, int hotkey = 0,
+ bool do_redraw_screen = false);
void list_tutorial_help(void);
// Actually defined in acr.cc; we may want to move this to command.cc
diff --git a/crawl-ref/source/dat/descript/monsters.txt b/crawl-ref/source/dat/descript/monsters.txt
index 4258190ae1..72c9b7126c 100644
--- a/crawl-ref/source/dat/descript/monsters.txt
+++ b/crawl-ref/source/dat/descript/monsters.txt
@@ -1,4 +1,21 @@
%%%%
+__N_suffix
+
+$"The insensible son of Pandu sank down till he reached the Naga kingdom.
+Nagas, furnished with fangs containing virulent venom, bit him by
+thousands. The vegetable poison, mingled in the blood of the son of the
+Wind god, was neutralised by the snake-poison. The serpents had bitten
+all over his frame, except his chest, the skin of which was so tough that
+their fangs could not penetrate it.
+"On regaining consciousness, the son of Kunti burst his bands and began
+to press the snakes down under the ground. A remnant fled for life, and
+going to their king Vasuki, represented, 'O king of snakes, a man drowned
+under the water, bound in chords of shrubs; probably he had drunk poison.
+For when he fell amongst us, he was insensible. But when we began to bite
+him, he regained his senses, and bursting his fetters, commenced laying
+at us. May it please Your Majesty to enquire who is.'"
+ -_The Mahabharata, Sambhava Parva, Section CXXVIII
+%%%%
&
You feel a lump in the pit of your stomach.
@@ -95,10 +112,22 @@ Gloorx Vloq
A shadowy figure clothed in profound darkness.
%%%%
+greater naga
+
+A hybrid; human from the chest up, with a scaly, muscular torso trailing off like that of a snake.
+
+It looks strong an agressive.
+%%%%
Green Death
A bloated form covered in oozing sores and exhaling clouds of lethal poison.
%%%%
+guardian naga
+
+A hybrid; human from the chest up, with a scaly, muscular torso trailing off like that of a snake.
+
+These nagas are often used as guardians by powerful creatures.
+%%%%
Harold
An evil human bounty hunter.
@@ -171,6 +200,10 @@ Orb Guardian
A huge and glowing purple creature, created by the Orb to defend itself.
%%%%
+pandemonium lord
+
+Powerful demons inhabitting the endless halls of Pandemonium, each is different, with its own set of strengths and weaknesses.
+%%%%
Pit Fiend
A huge winged fiend with incredibly tough skin.
@@ -894,7 +927,7 @@ stuck in its teeth.
%%%%
large skeleton
-A skeleton compelled to unlife by the exercise of necromancy.
+A large skeleton compelled to unlife by the exercise of necromancy.
%%%%
large zombie
@@ -978,21 +1011,18 @@ The embalmed and undead corpse of an ancient servant of darkness.
naga
A hybrid; human from the chest up, with a scaly, muscular torso trailing off like that of a snake.
+%%%%
+naga mage
-"The insensible son of Pandu sank down till he reached the Naga kingdom.
-Nagas, furnished with fangs containing virulent venom, bit him by
-thousands. The vegetable poison, mingled in the blood of the son of the
-Wind god, was neutralised by the snake-poison. The serpents had bitten
-all over his frame, except his chest, the skin of which was so tough that
-their fangs could not penetrate it.
-"On regaining consciousness, the son of Kunti burst his bands and began
-to press the snakes down under the ground. A remnant fled for life, and
-going to their king Vasuki, represented, 'O king of snakes, a man drowned
-under the water, bound in chords of shrubs; probably he had drunk poison.
-For when he fell amongst us, he was insensible. But when we began to bite
-him, he regained his senses, and bursting his fetters, commenced laying
-at us. May it please Your Majesty to enquire who is.'"
- -_The Mahabharata, Sambhava Parva, Section CXXVIII
+A hybrid; human from the chest up, with a scaly, muscular torso trailing off like that of a snake.
+
+An eldritch nimbus trails its motions.
+%%%%
+naga warrior
+
+A hybrid; human from the chest up, with a scaly, muscular torso trailing off like that of a snake.
+
+It bears scars of many past battles.
%%%%
necromancer
@@ -1246,6 +1276,14 @@ small snake
The lesser dungeon snake.
%%%%
+small skeleton
+
+A skeleton compelled to unlife by the exercise of necromancy.
+%%%%
+small zombie
+
+A corpse raised to undeath by necromancy.
+%%%%
smoke demon
A writhing cloud of smoke hanging in the air.
@@ -1318,7 +1356,7 @@ swamp worm
A large slimy worm, adept at swimming through the muck of this foul swamp.
%%%%
-tentacled monster
+tentacled monstrosity
A writhing mass of tentacles, all covered in putrid mucus.
%%%%
@@ -1462,7 +1500,3 @@ ynoxinul
A demon with shiny metallic scales.
%%%%
-zombie
-
-A corpse raised to undeath by necromancy.
-%%%%
diff --git a/crawl-ref/source/database.cc b/crawl-ref/source/database.cc
index 6de91c3a1e..86ae4b098c 100644
--- a/crawl-ref/source/database.cc
+++ b/crawl-ref/source/database.cc
@@ -165,6 +165,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)
+ {
+ dbKey = dbm_nextkey(database);
+ continue;
+ }
+
if (tpat.matches(key))
matches.push_back(key);
@@ -174,6 +180,37 @@ std::vector<std::string> database_find_keys(DBM *database,
return (matches);
}
+std::vector<std::string> database_find_bodies(DBM *database,
+ const std::string &regex,
+ bool ignore_case)
+{
+ text_pattern tpat(regex, ignore_case);
+ std::vector<std::string> matches;
+
+ datum dbKey = dbm_firstkey(database);
+
+ while (dbKey.dptr != NULL)
+ {
+ 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))
+ matches.push_back(key);
+
+ dbKey = dbm_nextkey(database);
+ }
+
+ return (matches);
+}
+
///////////////////////////////////////////////////////////////////////////
// Internal DB utility functions
static void trim_right(std::string &s)
@@ -425,7 +462,7 @@ std::string getLongDescription(const std::string &key)
return std::string((const char *)result.dptr, result.dsize);
}
-std::vector<std::string> getLongDescriptionByRegex(const std::string &regex)
+std::vector<std::string> getLongDescKeysByRegex(const std::string &regex)
{
if (!descriptionDB)
{
@@ -436,6 +473,17 @@ std::vector<std::string> getLongDescriptionByRegex(const std::string &regex)
return database_find_keys(descriptionDB, regex, true);
}
+std::vector<std::string> getLongDescBodiesByRegex(const std::string &regex)
+{
+ if (!descriptionDB)
+ {
+ std::vector<std::string> empty;
+ return (empty);
+ }
+
+ return database_find_bodies(descriptionDB, regex, true);
+}
+
static std::vector<std::string> description_txt_paths()
{
std::vector<std::string> txt_file_names;
diff --git a/crawl-ref/source/database.h b/crawl-ref/source/database.h
index b30d8814e6..357ab62a9a 100644
--- a/crawl-ref/source/database.h
+++ b/crawl-ref/source/database.h
@@ -43,9 +43,13 @@ 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 = false);
+std::vector<std::string> database_find_bodies(DBM *database,
+ const std::string &regex,
+ bool ignore_case = false);
std::string getLongDescription(const std::string &key);
-std::vector<std::string> getLongDescriptionByRegex(const std::string &regex);
+std::vector<std::string> getLongDescKeysByRegex(const std::string &regex);
+std::vector<std::string> getLongDescBodiesByRegex(const std::string &regex);
std::string getShoutString(const std::string &monst,
const std::string &suffix = "");
diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc
index 8555459057..c0d50a87e9 100644
--- a/crawl-ref/source/describe.cc
+++ b/crawl-ref/source/describe.cc
@@ -3555,10 +3555,12 @@ static std::string describe_draconian(const monsters *mon)
description += "scaled humanoid with wings.";
if (subsp != MONS_DRACONIAN)
- description += " " + describe_draconian_colour(subsp);
+ if (describe_draconian_colour(subsp) != "")
+ description += " " + describe_draconian_colour(subsp);
if (subsp != mon->type)
- description += " " + describe_draconian_role(mon);
+ if (describe_draconian_role(mon) != "")
+ description += " " + describe_draconian_role(mon);
return (description);
}
@@ -3593,6 +3595,13 @@ void describe_monsters(monsters& mons)
// -peterb 4/14/07
description << getLongDescription(mons.name(DESC_PLAIN));
+ unsigned char symbol = get_monster_data(mons.type)->showchar;
+
+ std::string symbol_prefix = "__";
+ symbol_prefix += symbol;
+ symbol_prefix += "_prefix";
+ description << getLongDescription(symbol_prefix);
+
// Now that the player has examined it, he knows it's a mimic.
if (mons_is_mimic(mons.type))
mons.flags |= MF_KNOWN_MIMIC;
@@ -3632,26 +3641,6 @@ void describe_monsters(monsters& mons)
case MONS_NAGA_WARRIOR:
case MONS_GUARDIAN_NAGA:
case MONS_GREATER_NAGA:
- switch (mons.type)
- {
- case MONS_GUARDIAN_NAGA:
- description << getLongDescription("naga")
- << "$These nagas are often used as guardians "
- "by powerful creatures.$";
- break;
- case MONS_GREATER_NAGA:
- description << getLongDescription("naga")
- << "$It looks strong and aggressive.$";
- break;
- case MONS_NAGA_MAGE:
- description << getLongDescription("naga")
- << "$An eldritch nimbus trails its motions.$";
- break;
- case MONS_NAGA_WARRIOR:
- description << getLongDescription("naga")
- << "$It bears scars of many past battles.$";
- break;
- }
if (you.species == SP_NAGA)
description << "It is particularly attractive.";
else
@@ -3718,6 +3707,12 @@ void describe_monsters(monsters& mons)
break;
}
+ description << "\n";
+ std::string symbol_suffix = "__";
+ symbol_suffix += symbol;
+ symbol_suffix += "_suffix";
+ description << getLongDescription(symbol_suffix);
+
#if DEBUG_DIAGNOSTICS
if (mons_class_flag( mons.type, M_SPELLCASTER ))