summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crawl-ref/source/command.cc9
-rw-r--r--crawl-ref/source/database.cc357
-rw-r--r--crawl-ref/source/database.h3
-rw-r--r--crawl-ref/source/debug.cc142
-rw-r--r--crawl-ref/source/debug.h3
-rw-r--r--crawl-ref/source/direct.cc18
-rw-r--r--crawl-ref/source/enum.h40
-rw-r--r--crawl-ref/source/insult.cc82
-rw-r--r--crawl-ref/source/insult.h3
-rw-r--r--crawl-ref/source/mon-data.h10
-rw-r--r--crawl-ref/source/mon-util.cc397
-rw-r--r--crawl-ref/source/mon-util.h35
-rw-r--r--crawl-ref/source/monplace.cc10
-rw-r--r--crawl-ref/source/monspeak.cc2783
-rw-r--r--crawl-ref/source/monstuff.cc25
-rw-r--r--crawl-ref/source/view.cc273
16 files changed, 1480 insertions, 2710 deletions
diff --git a/crawl-ref/source/command.cc b/crawl-ref/source/command.cc
index 3acf614088..167f93c0c3 100644
--- a/crawl-ref/source/command.cc
+++ b/crawl-ref/source/command.cc
@@ -535,7 +535,14 @@ static const char *targeting_help_1 =
"<w>!</w> : fire at target and stop there (may hit submerged creatures)\n"
"<w>p</w> : fire at Previous target (also <w>t</w>, <w>f</w>)\n"
"<w>:</w> : show/hide beam path\n"
- "<w>Shift-Dir</w> : shoot straight-line beam\n";
+ "<w>Shift-Dir</w> : shoot straight-line beam\n"
+#ifdef WIZARD
+ " \n"
+ "<h>Wizard targeting comands:</h>\n"
+ "<w>F</w>: make target friendly\n"
+ "<w>s</w>: force target to shout or speak\n"
+#endif
+;
static const char *targeting_help_2 =
"<h>Firing or throwing a missile:\n"
diff --git a/crawl-ref/source/database.cc b/crawl-ref/source/database.cc
index bf9c4fad14..939ffaa35d 100644
--- a/crawl-ref/source/database.cc
+++ b/crawl-ref/source/database.cc
@@ -25,12 +25,26 @@
db_list openDBList;
DBM *descriptionDB;
+DBM *shoutDB;
+DBM *speakDB;
+
#define DESC_BASE_NAME "descript"
#define DESC_TXT_DIR "descript"
#define DESC_DB (DESC_BASE_NAME ".db")
+#define SHOUT_BASE_NAME "shout"
+#define SHOUT_TXT (SHOUT_BASE_NAME ".txt")
+#define SHOUT_DB (SHOUT_BASE_NAME ".db")
+
+#define SPEAK_BASE_NAME "speak"
+#define SPEAK_TXT (SPEAK_BASE_NAME ".txt")
+#define SPEAK_DB (SPEAK_BASE_NAME ".db")
+
+
static std::vector<std::string> description_txt_paths();
static void generate_description_db();
+static void generate_shout_db();
+static void generate_speak_db();
void databaseSystemInit()
{
@@ -52,6 +66,30 @@ void databaseSystemInit()
if (!(descriptionDB = openDB(descriptionPath.c_str())))
end(1, true, "Failed to open DB: %s", descriptionPath.c_str());
}
+
+ if (!shoutDB)
+ {
+ std::string shoutPath = get_savedir_path(SHOUT_DB);
+ std::string shoutText = datafile_path(SHOUT_TXT);
+
+ check_newer(shoutPath, shoutText, generate_shout_db);
+
+ shoutPath.erase(shoutPath.length() - 3);
+ if (!(shoutDB = openDB(shoutPath.c_str())))
+ end(1, true, "Failed to open DB: %s", shoutPath.c_str());
+ }
+ if (!speakDB)
+ {
+ std::string speakPath = get_savedir_path(SPEAK_DB);
+ std::string speakText = datafile_path(SPEAK_TXT);
+
+ check_newer(speakPath, speakText, generate_speak_db);
+
+ speakPath.erase(speakPath.length() - 3);
+ if (!(speakDB = openDB(speakPath.c_str())))
+ end(1, true, "Failed to open DB: %s", speakPath.c_str());
+ }
+
}
void databaseSystemShutdown()
@@ -65,6 +103,9 @@ void databaseSystemShutdown()
descriptionDB = NULL;
}
+////////////////////////////////////////////////////////////////////////////
+// Main DB functions
+
// This is here, and is external, just for future expansion -- if we
// want to allow external modules to manage their own DB, they can
// use this for the sake of convenience. It's arguable that it's
@@ -95,63 +136,8 @@ datum database_fetch(DBM *database, const std::string &key)
return result;
}
-std::string getLongDescription(const std::string &key)
-{
- if (!descriptionDB)
- return ("");
-
- // We have to canonicalize the key (in case the user typed it
- // in and got the case wrong.)
- std::string canonical_key = key;
- lowercase(canonical_key);
-
- // Query the DB.
- datum result = database_fetch(descriptionDB, canonical_key);
-
- // Cons up a (C++) string to return. The caller must release it.
- return std::string((const char *)result.dptr, result.dsize);
-}
-
-static std::vector<std::string> description_txt_paths()
-{
- std::vector<std::string> txt_file_names;
- std::vector<std::string> paths;
-
- txt_file_names.push_back("features");
- txt_file_names.push_back("items");
- txt_file_names.push_back("monsters");
- txt_file_names.push_back("spells");
-
- for (int i = 0, size = txt_file_names.size(); i < size; i++)
- {
- std::string name = DESC_TXT_DIR;
- name += FILE_SEPARATOR;
- name += txt_file_names[i];
- name += ".txt";
-
- std::string txt_path = datafile_path(name);
- paths.push_back(txt_path);
- }
-
- return (paths);
-}
-
-static void store_descriptions(const std::string &in, const std::string &out);
-static void generate_description_db()
-{
- std::string db_path = get_savedir_path(DESC_BASE_NAME);
- std::string full_db_path = get_savedir_path(DESC_DB);
-
- std::vector<std::string> txt_paths = description_txt_paths();
-
- file_lock lock(get_savedir_path(DESC_BASE_NAME ".lk"), "wb");
- unlink( full_db_path.c_str() );
-
- for (int i = 0, size = txt_paths.size(); i < size; i++)
- store_descriptions(txt_paths[i], db_path);
- DO_CHMOD_PRIVATE(full_db_path.c_str());
-}
-
+///////////////////////////////////////////////////////////////////////////
+// Internal DB utility functions
static void trim_right(std::string &s)
{
s.erase(s.find_last_not_of(" \r\t\n") + 1);
@@ -176,7 +162,7 @@ static void add_entry(DBM *db, const std::string &k, std::string &v)
end(1, true, "Error storing %s", k.c_str());
}
-static void parse_descriptions(std::ifstream &inf, DBM *db)
+static void parse_text_db(std::ifstream &inf, DBM *db)
{
char buf[1000];
@@ -222,7 +208,7 @@ static void parse_descriptions(std::ifstream &inf, DBM *db)
add_entry(db, key, value);
}
-static void store_descriptions(const std::string &in, const std::string &out)
+static void store_text_db(const std::string &in, const std::string &out)
{
std::ifstream inf(in.c_str());
if (!inf)
@@ -230,7 +216,7 @@ static void store_descriptions(const std::string &in, const std::string &out)
if (DBM *db = dbm_open(out.c_str(), O_RDWR | O_CREAT, 0660))
{
- parse_descriptions(inf, db);
+ parse_text_db(inf, db);
dbm_close(db);
}
else
@@ -238,3 +224,254 @@ static void store_descriptions(const std::string &in, const std::string &out)
inf.close();
}
+
+static std::string chooseStrByWeight(std::string entry)
+{
+ std::vector<std::string> parts;
+ std::vector<int> weights;
+
+ std::vector<std::string> lines = split_string("\n", entry, false, true);
+
+ int total_weight = 0;
+ for (int i = 0, size = lines.size(); i < size; i++)
+ {
+ // Skip over multiple blank lines, and leading and trailing
+ // blank lines.
+ while (i < size && lines[i] == "")
+ i++;
+ if (i == size)
+ break;
+
+ int weight;
+ std::string part = "";
+
+ if (sscanf(lines[i].c_str(), "w:%d", &weight))
+ {
+ i++;
+ if (i == size)
+ return ("BUG, WEIGHT AT END OF ENTRY");
+ }
+ else
+ weight = 10;
+
+ total_weight += weight;
+
+ while (i < size && lines[i] != "")
+ {
+ part += lines[i++];
+ part += "\n";
+ }
+ trim_string(part);
+
+ parts.push_back(part);
+ weights.push_back(total_weight);
+ }
+
+ if (parts.size() == 0)
+ return("BUG, EMPTY ENTRY");
+
+ int choice = random2(total_weight);
+ std::string str = "";
+
+ for (int i = 0, size = parts.size(); i < size; i++)
+ if (choice < weights[i])
+ return(parts[i]);
+
+ return("BUG, NO STRING CHOSEN");
+}
+
+#define MAX_RECURSION_DEPTH 10
+#define MAX_REPLACEMENTS 100
+
+static std::string getRandomizedStr(DBM *database, const std::string &key,
+ const std::string &suffix,
+ int &num_replacements,
+ int recursion_depth = 0)
+{
+ recursion_depth++;
+ if (recursion_depth > MAX_RECURSION_DEPTH)
+ {
+ mpr("Too many nested replacements, bailing.", MSGCH_DIAGNOSTICS);
+
+ return "TOO MUCH RECURSION";
+ }
+
+ // We have to canonicalize the key (in case the user typed it
+ // in and got the case wrong.)
+ std::string canonical_key = key + suffix;
+ lowercase(canonical_key);
+
+ // Query the DB.
+ datum result = database_fetch(database, canonical_key);
+
+ if (result.dsize <= 0)
+ {
+ // Try ignoring the suffix
+ canonical_key = key;
+ lowercase(canonical_key);
+
+ // Query the DB.
+ result = database_fetch(database, canonical_key);
+
+ if (result.dsize <= 0)
+ return "";
+ }
+
+ // Cons up a (C++) string to return. The caller must release it.
+ std::string str = std::string((const char *)result.dptr, result.dsize);
+
+ str = chooseStrByWeight(str);
+
+ // Replace any "@foo@" markers that can be found in this database;
+ // those that can't be found are left alone for the caller to deal
+ // with.
+ std::string::size_type pos = str.find("@");
+ while (pos != std::string::npos)
+ {
+ num_replacements++;
+ if (num_replacements > MAX_REPLACEMENTS)
+ {
+ mpr("Too many string replacements, bailing.", MSGCH_DIAGNOSTICS);
+
+ return "TOO MANY REPLACEMENTS";
+ }
+
+ std::string::size_type end = str.find("@", pos + 1);
+ if (end == std::string::npos)
+ {
+ mpr("Unbalanced @, bailing.", MSGCH_DIAGNOSTICS);
+ break;
+ }
+
+ std::string marker_full = str.substr(pos, end - pos + 1);
+ std::string marker = str.substr(pos + 1, end - pos - 1);
+
+ std::string replacement =
+ getRandomizedStr(database, marker, suffix, num_replacements,
+ recursion_depth);
+
+ if (replacement == "")
+ // Nothing in database, leave it alone and go onto next @foo@
+ pos = str.find("@", end + 1);
+ else
+ {
+ str.replace(pos, marker_full.length(), replacement);
+
+ // Start search from pos rather than end + 1, so that if
+ // the replacement contains its own @foo@, we can replace
+ // that too.
+ pos = str.find("@", pos);
+ }
+ } // while (pos != std::string::npos)
+
+ return str;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Description DB specific functions.
+
+std::string getLongDescription(const std::string &key)
+{
+ if (!descriptionDB)
+ return ("");
+
+ // We have to canonicalize the key (in case the user typed it
+ // in and got the case wrong.)
+ std::string canonical_key = key;
+ lowercase(canonical_key);
+
+ // Query the DB.
+ datum result = database_fetch(descriptionDB, canonical_key);
+
+ // Cons up a (C++) string to return. The caller must release it.
+ return std::string((const char *)result.dptr, result.dsize);
+}
+
+static std::vector<std::string> description_txt_paths()
+{
+ std::vector<std::string> txt_file_names;
+ std::vector<std::string> paths;
+
+ txt_file_names.push_back("features");
+ txt_file_names.push_back("items");
+ txt_file_names.push_back("monsters");
+ txt_file_names.push_back("spells");
+/*
+ txt_file_names.push_back("shout");
+ txt_file_names.push_back("speak");
+*/
+ for (int i = 0, size = txt_file_names.size(); i < size; i++)
+ {
+ std::string name = DESC_TXT_DIR;
+ name += FILE_SEPARATOR;
+ name += txt_file_names[i];
+ name += ".txt";
+
+ std::string txt_path = datafile_path(name);
+
+ if (!txt_path.empty())
+ paths.push_back(txt_path);
+ }
+
+ return (paths);
+}
+
+static void generate_description_db()
+{
+ std::string db_path = get_savedir_path(DESC_BASE_NAME);
+ std::string full_db_path = get_savedir_path(DESC_DB);
+
+ std::vector<std::string> txt_paths = description_txt_paths();
+
+ file_lock lock(get_savedir_path(DESC_BASE_NAME ".lk"), "wb");
+ unlink( full_db_path.c_str() );
+
+ for (int i = 0, size = txt_paths.size(); i < size; i++)
+ store_text_db(txt_paths[i], db_path);
+ DO_CHMOD_PRIVATE(full_db_path.c_str());
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Shout DB specific functions.
+std::string getShoutString(const std::string &monst,
+ const std::string &suffix)
+{
+ int num_replacements = 0;
+
+ return getRandomizedStr(shoutDB, monst, suffix, num_replacements);
+}
+
+static void generate_shout_db()
+{
+ std::string db_path = get_savedir_path(SHOUT_BASE_NAME);
+ std::string full_db_path = get_savedir_path(SHOUT_DB);
+ std::string txt_path = datafile_path(SHOUT_TXT);
+
+ file_lock lock(get_savedir_path(SHOUT_BASE_NAME ".lk"), "wb");
+ unlink( full_db_path.c_str() );
+
+ store_text_db(txt_path, db_path);
+ DO_CHMOD_PRIVATE(full_db_path.c_str());
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Speak DB specific functions.
+std::string getSpeakString(const std::string &monst)
+{
+ int num_replacements = 0;
+
+ return getRandomizedStr(speakDB, monst, "", num_replacements);
+}
+
+static void generate_speak_db()
+{
+ std::string db_path = get_savedir_path(SPEAK_BASE_NAME);
+ std::string full_db_path = get_savedir_path(SPEAK_DB);
+ std::string txt_path = datafile_path(SPEAK_TXT);
+
+ file_lock lock(get_savedir_path(SPEAK_BASE_NAME ".lk"), "wb");
+ unlink( full_db_path.c_str() );
+
+ store_text_db(txt_path, db_path);
+ DO_CHMOD_PRIVATE(full_db_path.c_str());
+}
diff --git a/crawl-ref/source/database.h b/crawl-ref/source/database.h
index 5cd0923ebc..e1cb3f230e 100644
--- a/crawl-ref/source/database.h
+++ b/crawl-ref/source/database.h
@@ -42,4 +42,7 @@ datum database_fetch(DBM *database, const std::string &key);
std::string getLongDescription(const std::string &key);
+std::string getShoutString(const std::string &monst,
+ const std::string &suffix = "");
+std::string getSpeakString(const std::string &monst);
#endif
diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc
index a8f3c5c8ae..5edb16a701 100644
--- a/crawl-ref/source/debug.cc
+++ b/crawl-ref/source/debug.cc
@@ -51,11 +51,17 @@
#include "itemprop.h"
#include "item_use.h"
#include "items.h"
+
+#ifdef WIZARD
+#include "macro.h"
+#endif
+
#include "makeitem.h"
#include "mapdef.h"
#include "maps.h"
#include "misc.h"
#include "monplace.h"
+#include "monspeak.h"
#include "monstuff.h"
#include "mon-util.h"
#include "mutation.h"
@@ -402,6 +408,31 @@ void create_spec_monster_name(int x, int y)
const mons_spec mspec = mlist.get_monster(0);
if (!force_place && mspec.mid != -1)
{
+ // Only one ghost allowed per level
+ if (mspec.mid == MONS_PLAYER_GHOST)
+ {
+ for (int i = 0; i < MAX_MONSTERS; i++)
+ if (menv[i].type == MONS_PLAYER_GHOST
+ && menv[i].alive())
+ {
+ mpr("Only one player ghost per level allowed, "
+ "and this level already has one.");
+ return;
+ }
+ }
+ // Only one pandemonium lord allowed per level as well.
+ else if (mspec.mid == MONS_PANDEMONIUM_DEMON)
+ {
+ for (int i = 0; i < MAX_MONSTERS; i++)
+ if (menv[i].type == MONS_PANDEMONIUM_DEMON
+ && menv[i].alive())
+ {
+ mpr("Only one Pandemonium lord per level allowed, "
+ "and this level already has one.");
+ return;
+ }
+ }
+
coord_def place = find_newmons_square(mspec.mid, x, y);
if (in_bounds(place))
{
@@ -410,7 +441,58 @@ void create_spec_monster_name(int x, int y)
}
}
- dgn_place_monster(mspec, you.your_level, x, y, false);
+ if (!dgn_place_monster(mspec, you.your_level, x, y, false))
+ {
+ mpr("Unable to place monster");
+ return;
+ }
+
+ // Need to set a name for the player ghost
+ if (mspec.mid == MONS_PLAYER_GHOST)
+ {
+ unsigned char mid = mgrd[x][y];
+
+ if (mid >= MAX_MONSTERS || menv[mid].type != MONS_PLAYER_GHOST)
+ {
+ for (mid = 0; mid < MAX_MONSTERS; mid++)
+ if (menv[mid].type == MONS_PLAYER_GHOST
+ && menv[mid].alive())
+ break;
+ }
+
+ if (mid >= MAX_MONSTERS)
+ {
+ mpr("Couldn't find player ghost, probably going to crash.");
+ more();
+ return;
+ }
+
+ monsters &mon = menv[mid];
+ ghost_demon ghost;
+
+ ghost.name = "John Doe";
+ ghost.values.init(0);
+
+ char class_str[80];
+ mpr( "Make player ghost which class? ", MSGCH_PROMPT );
+ get_input_line( class_str, sizeof( class_str ) );
+
+ int class_id = get_class_index_by_abbrev(class_str);
+
+ if (class_id == -1)
+ class_id = get_class_index_by_name(class_str);
+
+ if (class_id == -1)
+ {
+ mpr("No such class, making it a Fighter.");
+ class_id = JOB_FIGHTER;
+ }
+ ghost.values[GVAL_CLASS] = class_id;
+
+ mon.set_ghost(ghost);
+
+ ghosts.push_back(ghost);
+ }
}
#endif
@@ -2464,6 +2546,64 @@ void debug_test_explore()
#endif
+#ifdef WIZARD
+extern void force_monster_shout(monsters* monster);
+
+void debug_make_monster_shout(monsters* mon)
+{
+
+ mpr("Make the monster (S)hout or (T)alk?", MSGCH_PROMPT);
+
+ char type = (char) getchm(KC_DEFAULT);
+ type = tolower(type);
+
+ if (type != 's' && type != 't')
+ {
+ canned_msg( MSG_OK );
+ return;
+ }
+
+ int num_times = debug_prompt_for_int("How many times? ", false);
+
+ if (num_times <= 0)
+ {
+ canned_msg( MSG_OK );
+ return;
+ }
+
+ if (type == 's')
+ {
+ if (silenced(mon->x, mon->y))
+ mpr("The monster is silenced and likely won't give any shouts.");
+ if (silenced(you.x_pos, you.y_pos))
+ mpr("You are silenced and likely won't hear any shouts.");
+
+ for (int i = 0; i < num_times; i++)
+ force_monster_shout(mon);
+ }
+ else
+ {
+ if (mon->invisible())
+ mpr("The monster is invisble and likely won't speak.");
+
+ if (silenced(you.x_pos, you.y_pos) && !silenced(mon->x, mon->y))
+ mpr("You are silenced but the monster isn't; you will "
+ "probably hear/see nothing.");
+ else if (!silenced(you.x_pos, you.y_pos) && silenced(mon->x, mon->y))
+ mpr("The monster is silenced and likely won't say anything.");
+ else if (silenced(you.x_pos, you.y_pos) && silenced(mon->x, mon->y))
+ mpr("Both you and the monster are silenced, so you likely "
+ "won't hear anything.");
+
+ for (int i = 0; i< num_times; i++)
+ mons_speaks(mon);
+ }
+
+ mpr("== Done ==");
+}
+#endif
+
+
#ifdef DEBUG_DIAGNOSTICS
// Map statistics generation.
diff --git a/crawl-ref/source/debug.h b/crawl-ref/source/debug.h
index e3bdad8ed2..6dd314f592 100644
--- a/crawl-ref/source/debug.h
+++ b/crawl-ref/source/debug.h
@@ -158,6 +158,9 @@ void debug_place_map();
void debug_test_explore();
void debug_dismiss_all_monsters();
+class monsters;
+void debug_make_monster_shout(monsters* mon);
+
#ifdef DEBUG_DIAGNOSTICS
void generate_map_stats();
class map_def;
diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc
index c3ea182be1..4d6515b94a 100644
--- a/crawl-ref/source/direct.cc
+++ b/crawl-ref/source/direct.cc
@@ -582,9 +582,26 @@ void direction(dist& moves, targeting_type restricts,
m.attitude = m.attitude == ATT_FRIENDLY? ATT_NEUTRAL :
m.attitude == ATT_HOSTILE? ATT_FRIENDLY :
ATT_HOSTILE;
+
+ // To update visual branding of friendlies. Only
+ // seem capabable of adding bolding, not removing it,
+ // though.
+ viewwindow(true, false);
}
break;
+
+ case CMD_TARGET_WIZARD_MAKE_SHOUT:
+ // Maybe we can skip this check...but it can't hurt
+ if (!you.wizard || !in_bounds(moves.tx, moves.ty))
+ break;
+ mid = mgrd[moves.tx][moves.ty];
+ if (mid == NON_MONSTER) // can put in terrain description here
+ break;
+
+ debug_make_monster_shout(&menv[mid]);
+ break;
#endif
+
case CMD_TARGET_DESCRIBE:
full_describe_square(moves.target());
@@ -1887,6 +1904,7 @@ command_type targeting_behaviour::get_command(int key)
#ifdef WIZARD
case 'F': return CMD_TARGET_WIZARD_MAKE_FRIENDLY;
+ case 's': return CMD_TARGET_WIZARD_MAKE_SHOUT;
#endif
case 'v': return CMD_TARGET_DESCRIBE;
case '?': return CMD_TARGET_HELP;
diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h
index 6b1c045bf2..a11550c525 100644
--- a/crawl-ref/source/enum.h
+++ b/crawl-ref/source/enum.h
@@ -757,6 +757,7 @@ enum command_type
CMD_TARGET_FIND_YOU,
CMD_TARGET_DESCRIBE,
CMD_TARGET_WIZARD_MAKE_FRIENDLY,
+ CMD_TARGET_WIZARD_MAKE_SHOUT,
CMD_TARGET_HELP,
// Disable/enable -more- prompts.
@@ -2492,23 +2493,24 @@ enum mon_flight_type
// These are now saved in an unsigned long in the monsters struct.
enum monster_flag_type
{
- MF_CREATED_FRIENDLY = 0x01, // no benefit from killing
- MF_GOD_GIFT = 0x02, // player not penalized by its death
- MF_BATTY = 0x04, // flutters like a bat
- MF_JUST_SUMMONED = 0x08, // monster skips next available action
- MF_TAKING_STAIRS = 0x10, // is following player through stairs
+ MF_CREATED_FRIENDLY = 0x01, // no benefit from killing
+ MF_GOD_GIFT = 0x02, // player not penalized by its death
+ MF_BATTY = 0x04, // flutters like a bat
+ MF_JUST_SUMMONED = 0x08, // monster skips next available action
+ MF_TAKING_STAIRS = 0x10, // is following player through stairs
- MF_INTERESTING = 0x20, // Player finds monster interesting
- MF_SEEN = 0x40, // Player already seen monster
- MF_DIVINE_PROTECTION = 0x80, // Monster has divine protection.
+ MF_INTERESTING = 0x20, // Player finds monster interesting
+ MF_SEEN = 0x40, // Player already seen monster
+ MF_DIVINE_PROTECTION = 0x80, // Monster has divine protection.
- MF_KNOWN_MIMIC = 0x100, // Mimic that has taken a swing at the PC,
- // or that the player has inspected with ?
- MF_BANISHED = 0x200, // Monster that has been banished.
- MF_HARD_RESET = 0x400, // Summoned, should not drop gear on reset
- MF_CONVERT_ATTEMPT = 0x800, // Orcs only: seen player and was converted
- // (or not)
- MF_WAS_IN_VIEW = 0x1000 // Was in view during previous turn
+ MF_KNOWN_MIMIC = 0x100, // Mimic that has taken a swing at the PC,
+ // or that the player has inspected with ?
+ MF_BANISHED = 0x200, // Monster that has been banished.
+ MF_HARD_RESET = 0x400, // Summoned, should not drop gear on reset
+ MF_CONVERT_ATTEMPT = 0x800, // Orcs only: seen player and was converted
+ // (or not)
+ MF_WAS_IN_VIEW = 0x1000, // Was in view during previous turn
+ MF_BAND_MEMBER = 0x2000 // Created as a member of a band
};
enum mon_dam_level_type
@@ -3109,6 +3111,14 @@ enum shout_type
S_CROAK, // frog croak
S_GROWL, // for bears
S_HISS, // for snakes and lizards
+
+ // Loudness setting for shouts that are only defined in dat/shout.txt
+ S_VERY_SOFT,
+ S_SOFT,
+ S_NORMAL,
+ S_LOUD,
+ S_VERY_LOUD,
+
NUM_SHOUTS,
S_RANDOM
};
diff --git a/crawl-ref/source/insult.cc b/crawl-ref/source/insult.cc
index 160ef04a5e..24bbd93f0b 100644
--- a/crawl-ref/source/insult.cc
+++ b/crawl-ref/source/insult.cc
@@ -38,11 +38,9 @@ void init_cap(char * str)
str[0] = toupper( str[0] );
}
-void imp_taunt( const monsters *mons )
+std::string imp_taunt_str()
{
char buff[80];
- const std::string mon_name = mons->name(DESC_CAP_THE);
-
snprintf( buff, sizeof(buff),
"%s, thou %s!",
random2(7) ? run_away() : give_up(),
@@ -50,41 +48,31 @@ void imp_taunt( const monsters *mons )
init_cap( buff );
+ return (buff);
+}
+
+void imp_taunt( const monsters *mons )
+{
+ std::string str = imp_taunt_str();
+ std::string mon_name = mons->name(DESC_CAP_THE);
+
+
// XXX: Not pretty, but stops truncation...
- if (mon_name.length() + 11 + strlen( buff ) >= 79)
+ if (mon_name.length() + 11 + str.length() >= 79)
{
mprf(MSGCH_TALK, "%s shouts:", mon_name.c_str() );
- mprf(MSGCH_TALK, "%s", buff );
+ mprf(MSGCH_TALK, "%s", str.c_str() );
}
else
{
- mprf(MSGCH_TALK, "%s shouts, \"%s\"", mon_name.c_str(), buff );
+ mprf(MSGCH_TALK, "%s shouts, \"%s\"", mon_name.c_str(),
+ str.c_str() );
}
}
-void demon_taunt( const monsters *mons )
+std::string demon_taunt_str()
{
- static const char * sound_list[] =
- {
- "says", // actually S_SILENT
- "shouts",
- "barks",
- "shouts",
- "roars",
- "screams",
- "bellows",
- "screeches",
- "buzzes",
- "moans",
- "whines",
- "croaks",
- "growls",
- };
-
char buff[80];
- const std::string mon_name = mons->name(DESC_CAP_THE);
- const char *voice = sound_list[ mons_shouts(mons->type) ];
-
if (coinflip())
{
snprintf( buff, sizeof(buff),
@@ -126,15 +114,49 @@ void demon_taunt( const monsters *mons )
init_cap( buff );
+ return (buff);
+}
+
+void demon_taunt( const monsters *mons )
+{
+ std::string str = demon_taunt_str();
+ const std::string mon_name = mons->name(DESC_CAP_THE);
+
+ static const char * sound_list[] =
+ {
+ "says", // actually S_SILENT
+ "shouts",
+ "barks",
+ "shouts",
+ "roars",
+ "screams",
+ "bellows",
+ "screeches",
+ "buzzes",
+ "moans",
+ "whines",
+ "croaks",
+ "growls",
+ "hisses",
+ "breathes", // S_VERY_SOFT
+ "whispers", // S_SOFT
+ "says", // S_NORMAL
+ "shouts", // S_LOUD
+ "screams" // S_VERY_LOUD
+ };
+
+ const char *voice = sound_list[ mons_shouts(mons->type) ];
+
// XXX: Not pretty, but stops truncation...
- if (mon_name.length() + strlen(voice) + strlen(buff) + 5 >= 79)
+ if (mon_name.length() + strlen(voice) + str.length() + 5 >= 79)
{
mprf(MSGCH_TALK, "%s %s:", mon_name.c_str(), voice );
- mprf(MSGCH_TALK, "%s", buff);
+ mprf(MSGCH_TALK, "%s", str.c_str());
}
else
{
- mprf(MSGCH_TALK, "%s %s, \"%s\"", mon_name.c_str(), voice, buff );
+ mprf(MSGCH_TALK, "%s %s, \"%s\"", mon_name.c_str(), voice,
+ str.c_str() );
}
}
diff --git a/crawl-ref/source/insult.h b/crawl-ref/source/insult.h
index d29b66acb4..adeed139f3 100644
--- a/crawl-ref/source/insult.h
+++ b/crawl-ref/source/insult.h
@@ -8,4 +8,7 @@ void demon_taunt( const monsters *mons );
const char * generic_insult(void);
const char * racial_insult(void);
+std::string imp_taunt_str();
+std::string demon_taunt_str();
+
#endif
diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h
index c66b117164..fc70f66c00 100644
--- a/crawl-ref/source/mon-data.h
+++ b/crawl-ref/source/mon-data.h
@@ -1484,7 +1484,7 @@
{
MONS_CRYSTAL_GOLEM, '8', WHITE, "crystal golem",
- M_SEE_INVIS,
+ M_SEE_INVIS | M_SPEAKS,
MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC,
0, 10, MONS_CLAY_GOLEM, MONS_CRYSTAL_GOLEM, MH_NONLIVING, MAG_IMMUNE,
{ {AT_HIT, AF_PLAIN, 40}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
@@ -3844,7 +3844,7 @@
{
MONS_ELECTRIC_GOLEM, '8', LIGHTCYAN, "electric golem",
- M_SPELLCASTER | M_SEE_INVIS,
+ M_SPELLCASTER | M_SEE_INVIS | M_SPEAKS,
MR_RES_ELEC | MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD,
0, 10, MONS_CLAY_GOLEM, MONS_ELECTRIC_GOLEM, MH_NONLIVING, -8,
{ {AT_HIT, AF_ELEC, 15}, {AT_HIT, AF_ELEC, 15}, {AT_HIT, AF_PLAIN, 15}, {AT_HIT, AF_PLAIN, 15} },
@@ -4383,7 +4383,7 @@
{
MONS_ORANGE_STATUE, '8', LIGHTRED, "orange crystal statue",
- M_SPECIAL_ABILITY,
+ M_SPECIAL_ABILITY | M_SPEAKS,
MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC,
0, 10, MONS_CLAY_GOLEM, MONS_ORANGE_STATUE, MH_NONLIVING, MAG_IMMUNE,
{ {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
@@ -4395,7 +4395,7 @@
{
MONS_SILVER_STATUE, '8', WHITE, "silver statue",
- M_SPECIAL_ABILITY,
+ M_SPECIAL_ABILITY | M_SPEAKS,
MR_RES_POISON | MR_RES_FIRE | MR_RES_COLD | MR_RES_ELEC,
0, 10, MONS_CLAY_GOLEM, MONS_SILVER_STATUE, MH_NONLIVING, MAG_IMMUNE,
{ {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
@@ -4407,7 +4407,7 @@
{
MONS_ICE_STATUE, '8', LIGHTBLUE, "ice statue",
- M_SPELLCASTER,
+ M_SPELLCASTER | M_SPEAKS,
MR_RES_POISON | MR_VUL_FIRE | MR_RES_COLD | MR_RES_ELEC,
0, 10, MONS_CLAY_GOLEM, MONS_ICE_STATUE, MH_NONLIVING, MAG_IMMUNE,
{ {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0}, {AT_NONE, AF_PLAIN, 0} },
diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc
index 5b8a200e5f..627f80e6e3 100644
--- a/crawl-ref/source/mon-util.cc
+++ b/crawl-ref/source/mon-util.cc
@@ -34,6 +34,7 @@
#include "debug.h"
#include "delay.h"
#include "dgnevent.h"
+#include "insult.h"
#include "itemname.h"
#include "itemprop.h"
#include "items.h"
@@ -2067,7 +2068,7 @@ bool mons_has_ranged_attack( const monsters *mon )
const item_def *primary = mnc->mslot_item(MSLOT_WEAPON);
const item_def *missile = mnc->missiles();
- if (!missile && weapon != primary
+ if (!missile && weapon != primary && primary
&& get_weapon_brand(*primary) == SPWPN_RETURNING)
{
return (true);
@@ -4654,3 +4655,397 @@ void mon_enchant::set_duration(const monsters *mons, const mon_enchant *added)
if (duration > maxduration)
maxduration = duration;
}
+
+// Replaces the "@foo@" strings in monster shout and monster speak
+// definitions.
+std::string do_mon_str_replacements(const std::string in_msg,
+ const monsters* monster)
+{
+ std::string msg = in_msg;
+
+ description_level_type nocap, cap;
+
+ if (monster->attitude == ATT_FRIENDLY && player_monster_visible(monster))
+ {
+ nocap = DESC_PLAIN;
+ cap = DESC_PLAIN;
+
+ msg = replace_all(msg, "@the_something@", "your @the_something@");
+ msg = replace_all(msg, "@The_something@", "Your @The_something@");
+ msg = replace_all(msg, "@the_monster@", "your @the_monster@");
+ msg = replace_all(msg, "@The_monster@", "Your @the_monster@");
+ }
+ else
+ {
+ nocap = DESC_NOCAP_THE;
+ cap = DESC_CAP_THE;
+ }
+
+ if (see_grid(monster->x, monster->y))
+ {
+ dungeon_feature_type feat = grd[monster->x][monster->y];
+ if (feat < MINMOVE || feat >= NUM_REAL_FEATURES)
+ msg = replace_all(msg, "@surface@", "buggy surface");
+ else if (feat == DNGN_LAVA)
+ msg = replace_all(msg, "@surface@", "lava");
+ else if (feat == DNGN_DEEP_WATER || feat == DNGN_SHALLOW_WATER)
+ msg = replace_all(msg, "@surface@", "water");
+ else if (feat >= DNGN_ALTAR_ZIN && feat < DNGN_BLUE_FOUNTAIN)
+ msg = replace_all(msg, "@surface@", "altar");
+ else
+ msg = replace_all(msg, "@surface@", "ground");
+
+ msg = replace_all(msg, "@feature@", raw_feature_description(feat));
+ }
+ else
+ {
+ msg = replace_all(msg, "@surface@", "buggy unseen surface");
+ msg = replace_all(msg, "@feature@", "buggy unseen feature");
+ }
+
+ msg = replace_all(msg, "@player_name@", you.your_name);
+
+ if (player_monster_visible(monster))
+ {
+ std::string something = monster->name(DESC_PLAIN);
+ msg = replace_all(msg, "@something@", something);
+ msg = replace_all(msg, "@a_something@", monster->name(DESC_NOCAP_A));
+ msg = replace_all(msg, "@the_something@", monster->name(nocap));
+
+ something[0] = toupper(something[0]);
+ msg = replace_all(msg, "@Something@", something);
+ msg = replace_all(msg, "@A_something@", monster->name(DESC_CAP_A));
+ msg = replace_all(msg, "@The_something@", monster->name(cap));
+ }
+ else
+ {
+ msg = replace_all(msg, "@something@", "something");
+ msg = replace_all(msg, "@a_something@", "something");
+ msg = replace_all(msg, "@the_something@", "something");
+
+ msg = replace_all(msg, "@Something@", "Something");
+ msg = replace_all(msg, "@A_something@", "Something");
+ msg = replace_all(msg, "@The_something@", "Something");
+ }
+
+ std::string plain = monster->name(DESC_PLAIN);
+ msg = replace_all(msg, "@monster@", plain);
+ msg = replace_all(msg, "@a_monster@", monster->name(DESC_NOCAP_A));
+ msg = replace_all(msg, "@the_monster@", monster->name(nocap));
+
+ plain[0] = toupper(plain[0]);
+ msg = replace_all(msg, "@Monster@", plain);
+ msg = replace_all(msg, "@A_monster@", monster->name(DESC_CAP_A));
+ msg = replace_all(msg, "@The_monster@", monster->name(cap));
+
+ msg = replace_all(msg, "@possessive@",
+ mons_pronoun(monster->type, 3));
+ msg = replace_all(msg, "@pronoun@",
+ mons_pronoun(monster->type, 0));
+
+ msg = replace_all(msg, "@imp_taunt@", imp_taunt_str());
+ msg = replace_all(msg, "@demon_taunt@", demon_taunt_str());
+
+ static const char * sound_list[] =
+ {
+ "says", // actually S_SILENT
+ "shouts",
+ "barks",
+ "shouts",
+ "roars",
+ "screams",
+ "bellows",
+ "screeches",
+ "buzzes",
+ "moans",
+ "whines",
+ "croaks",
+ "growls",
+ "hisses",
+ "breathes", // S_VERY_SOFT
+ "whispers", // S_SOFT
+ "says", // S_NORMAL
+ "shouts", // S_LOUD
+ "screams" // S_VERY_LOUD
+ };
+
+ if (mons_shouts(monster->type) >= NUM_SHOUTS)
+ {
+ mpr("Invalid @says@ type.", MSGCH_DIAGNOSTICS);
+ msg = replace_all(msg, "@says@", "bugilly says");
+ }
+ else
+ msg = replace_all(msg, "@says@",
+ sound_list[mons_shouts(monster->type)]);
+
+ // The proper possessive for a word ending in an "s" is to
+ // put an appostraphe after the "s": "Chris" -> "Chris'",
+ // not "Chris" -> "Chris's". Stupid English language...
+ msg = replace_all(msg, "s's", "s'");
+
+ return msg;
+}
+
+static mon_body_shape get_ghost_shape(const monsters *mon)
+{
+ const ghost_demon &ghost = *(mon->ghost);
+
+ switch(ghost.values[GVAL_SPECIES])
+ {
+ case SP_NAGA:
+ return (MON_SHAPE_NAGA);
+
+ case SP_CENTAUR:
+ return (MON_SHAPE_CENTAUR);
+
+ case SP_KENKU:
+ return (MON_SHAPE_HUMANOID_WINGED);
+
+ case SP_RED_DRACONIAN:
+ case SP_WHITE_DRACONIAN:
+ case SP_GREEN_DRACONIAN:
+ case SP_GOLDEN_DRACONIAN:
+ case SP_GREY_DRACONIAN:
+ case SP_BLACK_DRACONIAN:
+ case SP_PURPLE_DRACONIAN:
+ case SP_MOTTLED_DRACONIAN:
+ case SP_PALE_DRACONIAN:
+ case SP_UNK0_DRACONIAN:
+ case SP_UNK1_DRACONIAN:
+ case SP_BASE_DRACONIAN:
+ return (MON_SHAPE_HUMANOID_TAILED);
+ }
+
+ return (MON_SHAPE_HUMANOID);
+}
+
+mon_body_shape get_mon_shape(const monsters *mon)
+{
+ if (mon->type == MONS_PLAYER_GHOST)
+ return get_ghost_shape(mon);
+ else if (mons_is_zombified(mon))
+ return get_mon_shape(mon->number);
+ else
+ return get_mon_shape(mon->type);
+}
+
+mon_body_shape get_mon_shape(const int type)
+{
+ switch(mons_char(type))
+ {
+ case 'a': // ants and cockroaches
+ return(MON_SHAPE_INSECT);
+ case 'b': // bats and butterflys
+ if (type == MONS_BUTTERFLY)
+ return(MON_SHAPE_INSECT_WINGED);
+ else
+ return(MON_SHAPE_BAT);
+ case 'c': // centaurs
+ return(MON_SHAPE_CENTAUR);
+ case 'd': // draconions and drakes
+ if (mons_genus(type) == MONS_DRACONIAN ||
+ mons_class_flag(type, M_HUMANOID))
+ {
+ if (mons_class_flag(type, M_FLIES))
+ return(MON_SHAPE_HUMANOID_WINGED_TAILED);
+ else
+ return(MON_SHAPE_HUMANOID_TAILED);
+ }
+ else if (mons_class_flag(type, M_FLIES))
+ return(MON_SHAPE_QUADRUPED_WINGED);
+ else
+ return(MON_SHAPE_QUADRUPED);
+ case 'e': // elves
+ return(MON_SHAPE_HUMANOID);
+ case 'f': // fungi
+ return(MON_SHAPE_FUNGUS);
+ case 'g': // gargoyles, gnolls, goblins and hobgoblins
+ if (type == MONS_GARGOYLE)
+ return(MON_SHAPE_HUMANOID_WINGED_TAILED);
+ else
+ return(MON_SHAPE_HUMANOID);
+ case 'h': // hounds
+ case 'j': // jackals
+ return(MON_SHAPE_QUADRUPED);
+ case 'k': // killer bees
+ return(MON_SHAPE_INSECT_WINGED);
+ case 'l': // lizards
+ return(MON_SHAPE_QUADRUPED);
+ case 'm': // minotaurs, manticores, and snails/slugs/etc
+ if (type == MONS_MINOTAUR)
+ return(MON_SHAPE_HUMANOID);
+ else if (type == MONS_MANTICORE)
+ return(MON_SHAPE_QUADRUPED);
+ else
+ return(MON_SHAPE_SNAIL);
+ case 'n': // necrophages and ghouls
+ return(MON_SHAPE_HUMANOID);
+ case 'o': // orcs
+ return(MON_SHAPE_HUMANOID);
+ case 'p': // ghosts
+ if (type != MONS_INSUBSTANTIAL_WISP &&
+ type != MONS_PLAYER_GHOST)
+ return(MON_SHAPE_HUMANOID);
+ case 'q': // quasists
+ return(MON_SHAPE_HUMANOID_TAILED);
+ case 'r': // rodents
+ return(MON_SHAPE_QUADRUPED);
+ case 's': // arachnids and centidpeds
+ if (type == MONS_GIANT_CENTIPEDE)
+ return(MON_SHAPE_CENTIPEDE);
+ else
+ return(MON_SHAPE_ARACHNID);
+ case 'u': // ugly things are humanoid???
+ return(MON_SHAPE_HUMANOID);
+ case 'v': // vortices and elementals
+ return(MON_SHAPE_MISC);
+ case 'w': // worms
+ return(MON_SHAPE_SNAKE);
+ case 'x': // small abominations
+ return(MON_SHAPE_MISC);
+ case 'y': // winged insects
+ return(MON_SHAPE_INSECT_WINGED);
+ case 'z': // small skeletons
+ if (type == MONS_SKELETAL_WARRIOR)
+ return(MON_SHAPE_HUMANOID);
+ else
+ // constructed type, not enough info to determine shape
+ return(MON_SHAPE_MISC);
+ case 'A': // angelic beings
+ return(MON_SHAPE_HUMANOID_WINGED);
+ case 'B': // beetles
+ return(MON_SHAPE_INSECT);
+ case 'C': // giants
+ return(MON_SHAPE_HUMANOID);
+ case 'D': // dragons
+ if (mons_class_flag(type, M_FLIES))
+ return(MON_SHAPE_QUADRUPED_WINGED);
+ else
+ return(MON_SHAPE_QUADRUPED);
+ case 'E': // effreets
+ return(MON_SHAPE_HUMANOID);
+ case 'F': // frogs
+ return(MON_SHAPE_QUADRUPED_TAILLESS);
+ case 'G': // floating eyeballs and orbs
+ return(MON_SHAPE_ORB);
+ case 'H': // hippogriffs and griffns
+ return(MON_SHAPE_QUADRUPED_WINGED);
+ case 'I': // ice beasts
+ return(MON_SHAPE_QUADRUPED);
+ case 'J': // jellies and jellyfish
+ return(MON_SHAPE_BLOB);
+ case 'K': // kobolds
+ return(MON_SHAPE_HUMANOID);
+ case 'L': // liches
+ return(MON_SHAPE_HUMANOID);
+ case 'M': // mummies
+ return(MON_SHAPE_HUMANOID);
+ case 'N': // nagas
+ return(MON_SHAPE_NAGA);
+ case 'O': // ogres
+ return(MON_SHAPE_HUMANOID);
+ case 'P': // plants
+ return(MON_SHAPE_PLANT);
+ case 'Q': // queen insects
+ if (type == MONS_QUEEN_BEE)
+ return(MON_SHAPE_INSECT_WINGED);
+ else
+ return(MON_SHAPE_INSECT);
+ case 'R': // rakshasa; humanoid?
+ return(MON_SHAPE_HUMANOID);
+ case 'S': // snakes
+ return(MON_SHAPE_SNAKE);
+ case 'T': // trolls
+ return(MON_SHAPE_HUMANOID);
+ case 'U': // bears
+ return(MON_SHAPE_QUADRUPED_TAILLESS);
+ case 'V': // vampires
+ return(MON_SHAPE_HUMANOID);
+ case 'W': // wraiths, humanoid if not a spectral thing
+ if (type == MONS_SPECTRAL_THING)
+ // constructed type, not enough info to determine shape
+ return(MON_SHAPE_MISC);
+ else
+ return(MON_SHAPE_HUMANOID);
+ case 'X': // large abominations
+ return(MON_SHAPE_MISC);
+ case 'Y': // yaks and sheep
+ if (type == MONS_SHEEP)
+ return(MON_SHAPE_QUADRUPED_TAILLESS);
+ else
+ return(MON_SHAPE_QUADRUPED);
+ case 'Z': // constructed type, not enough info to determine shape
+ return(MON_SHAPE_MISC);
+ case ';': // Fish and eels
+ if (type == MONS_ELECTRICAL_EEL)
+ return(MON_SHAPE_SNAKE);
+ else
+ return (MON_SHAPE_FISH);
+
+ // The various demons, plus some golems and statues. And humanoids.
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '&':
+ case '8':
+ case '@':
+ // Assume demon has wings if it can fly.
+ bool flies = mons_class_flag(type, M_FLIES);
+
+ // Assume demon has a tail if it has a sting attack or a
+ // tail slap attack.
+ monsterentry *mon_data = get_monster_data(type);
+ bool tailed = false;
+ for (int i = 0; i < 4; i++)
+ if (mon_data->attack[i].type == AT_STING ||
+ mon_data->attack[i].type == AT_TAIL_SLAP)
+ {
+ tailed = true;
+ break;
+ }
+
+ if (flies && tailed)
+ return(MON_SHAPE_HUMANOID_WINGED_TAILED);
+ else if (flies && !tailed)
+ return(MON_SHAPE_HUMANOID_WINGED);
+ else if (!flies && tailed)
+ return(MON_SHAPE_HUMANOID_TAILED);
+ else
+ return(MON_SHAPE_HUMANOID);
+ }
+
+ return(MON_SHAPE_MISC);
+}
+
+std::string get_mon_shape_str(const monsters *mon)
+{
+ return get_mon_shape_str(get_mon_shape(mon));
+}
+
+std::string get_mon_shape_str(const int type)
+{
+ return get_mon_shape_str(get_mon_shape(type));
+}
+
+std::string get_mon_shape_str(const mon_body_shape shape)
+{
+ ASSERT(shape >= MON_SHAPE_HUMANOID && shape <= MON_SHAPE_MISC);
+
+ if (shape < MON_SHAPE_HUMANOID || shape > MON_SHAPE_MISC)
+ return("buggy shape");
+
+ static const char *shape_names[] =
+ {
+ "humanoid", "winged humanoid", "tailed humanoid",
+ "winged tailed humanoid", "centaur", "naga",
+ "quadruped", "tailless quadruped", "winged quadruped",
+ "bat", "snake", "fish", "insect", "winged insect",
+ "arachnid", "centipede", "snail", "plant", "fungus", "orb",
+ "blob", "misc"
+ };
+
+ return (shape_names[shape]);
+}
+
diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h
index 1461c5bd88..61e9d9099b 100644
--- a/crawl-ref/source/mon-util.h
+++ b/crawl-ref/source/mon-util.h
@@ -371,4 +371,39 @@ monster_type random_monster_at_grid(int grid);
monster_type get_monster_by_name(std::string name, bool exact = false);
+std::string do_mon_str_replacements(const std::string msg,
+ const monsters* monster);
+
+enum mon_body_shape {
+ MON_SHAPE_HUMANOID,
+ MON_SHAPE_HUMANOID_WINGED,
+ MON_SHAPE_HUMANOID_TAILED,
+ MON_SHAPE_HUMANOID_WINGED_TAILED,
+ MON_SHAPE_CENTAUR,
+ MON_SHAPE_NAGA,
+ MON_SHAPE_QUADRUPED,
+ MON_SHAPE_QUADRUPED_TAILLESS,
+ MON_SHAPE_QUADRUPED_WINGED,
+ MON_SHAPE_BAT,
+ MON_SHAPE_SNAKE, // Including eels and worms
+ MON_SHAPE_FISH,
+ MON_SHAPE_INSECT,
+ MON_SHAPE_INSECT_WINGED,
+ MON_SHAPE_ARACHNID,
+ MON_SHAPE_CENTIPEDE,
+ MON_SHAPE_SNAIL,
+ MON_SHAPE_PLANT,
+ MON_SHAPE_FUNGUS,
+ MON_SHAPE_ORB,
+ MON_SHAPE_BLOB,
+ MON_SHAPE_MISC
+};
+
+mon_body_shape get_mon_shape(const monsters *mon);
+mon_body_shape get_mon_shape(const int type);
+
+std::string get_mon_shape_str(const monsters *mon);
+std::string get_mon_shape_str(const int type);
+std::string get_mon_shape_str(const mon_body_shape shape);
+
#endif
diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc
index 4d80b20520..7ad2cf81f3 100644
--- a/crawl-ref/source/monplace.cc
+++ b/crawl-ref/source/monplace.cc
@@ -618,11 +618,17 @@ bool place_monster(int &id, int mon_type, int power, beh_type behaviour,
if (proximity == PROX_NEAR_STAIRS)
return (true);
+ // Not PROX_NEAR_STAIRS, so will it will be part of a band, if there
+ // is any.
+ if (band_size > 1)
+ menv[id].flags |= MF_BAND_MEMBER;
+
// (5) for each band monster, loop call to place_monster_aux().
for(i = 1; i < band_size; i++)
{
- place_monster_aux( band_monsters[i], behaviour, target, px, py,
- lev_mons, extra, false, dur);
+ id = place_monster_aux( band_monsters[i], behaviour, target, px, py,
+ lev_mons, extra, false, dur);
+ menv[id].flags |= MF_BAND_MEMBER;
}
// placement of first monster, at least, was a success.
diff --git a/crawl-ref/source/monspeak.cc b/crawl-ref/source/monspeak.cc
index b79a7f9aec..90a7556e66 100644
--- a/crawl-ref/source/monspeak.cc
+++ b/crawl-ref/source/monspeak.cc
@@ -23,6 +23,7 @@
#include "externs.h"
#include "beam.h"
+#include "database.h"
#include "debug.h"
#include "fight.h"
#include "insult.h"
@@ -38,187 +39,128 @@
#include "stuff.h"
#include "view.h"
-struct mon_dialogue
+static std::string get_speak_string(const std::vector<std::string> prefixes,
+ const std::string key,
+ const monsters *monster)
{
- monster_type speaker;
- const char **silenced;
- const char **confused;
- const char **confused_friend;
- const char **fleeing;
- const char **fleeing_friend;
- const char **friendly;
- const char **hostile; // Most common.
-};
-
-static const char *murray_silenced[] =
-{
- "%s rolls in a circle.",
- "%s rolls around.",
- "%s spins like a top.",
- "%s grins evilly.",
- "%s seems to say something.",
- "%s says something you can't hear. It was probably not a compliment.",
- NULL
-};
-
-static const char *murray_hostile[] =
-{
- "%s rolls in a circle.",
- "%s rolls around.",
- "%s spins like a top.",
- "%s grins evilly.",
- "%s laughs evilly.",
- "%s cackles, \"I will rule the world!\"",
- "%s shouts, \"Give me your head, so I can impale it on a pike!\"",
- "%s's teeth chatter loudly.",
- "%s yells, \"I'm a mighty demonic power!\"",
- "%s asks, \"How could you choose the Orb over me, your best friend?\"",
- "%s shouts, \"Let the forces of evil and voodoo overcome you!\"",
- "%s screams, \"If I had legs, you would be dead twenty times over!\"",
- "%s yells, \"My visage is famous all over the dungeon!\"",
- "%s says, \"You're the second biggest fool I've ever met!\"",
- NULL
-};
+ std::string prefix = "";
+ const int size = prefixes.size();
+ for (int i = 0; i < size; i++)
+ {
+ prefix += prefixes[i];
+ prefix += " ";
+ }
-static mon_dialogue vox_populi[] =
-{
- { MONS_MURRAY, murray_silenced, NULL, NULL, NULL, NULL, NULL,
- murray_hostile },
-};
+ std::string msg = "";
+ msg = getSpeakString(prefix + key);
+ if (msg != "")
+ return msg;
-static const mon_dialogue *find_dialogue(const monsters *monster)
-{
- for (unsigned i = 0; i < sizeof(vox_populi) / sizeof(*vox_populi); ++i)
- if (vox_populi[i].speaker == monster->type)
- return (&vox_populi[i]);
- return (NULL);
-}
+ // Combinations of prefixes by threes
+ if (size >= 3)
+ {
+ for (int i = 0; i < (size - 2); i++)
+ for (int j = i + 1; j < (size - 1); j++)
+ for (int k = j + 1; k < size; k++)
+ {
+ prefix = prefixes[i] + " ";
+ prefix += prefixes[j] + " ";
+ prefix += prefixes[k] + " ";
-static bool say_dialogue(const monsters *monster,
- const char **dialogue)
-{
- if (!dialogue)
- return (false);
+ msg = getSpeakString("default " + prefix + key);
+ if (msg != "")
+ return msg;
+ }
+ }
- int nitems = 0;
- for (const char **run = dialogue; *run; ++run, ++nitems)
- ;
+ // Combinations of prefixes by twos
+ if (size >= 2)
+ {
+ for (int i = 0; i < (size - 1); i++)
+ for (int j = i + 1; j < size; j++)
+ {
+ prefix = prefixes[i] + " ";
+ prefix += prefixes[j] + " ";
- const char *chosen = nitems? dialogue[random2(nitems)] : NULL;
+ msg = getSpeakString("default " + prefix + key);
+ if (msg != "")
+ return msg;
+ }
+ }
- if (chosen && *chosen)
+ // Prefixes singly
+ if (size >= 1)
{
- mprf(MSGCH_TALK, chosen, monster->name(DESC_CAP_THE).c_str());
- return (true);
+ for (int i = 0; i < size; i++)
+ {
+ prefix = prefixes[i] + " ";
+
+ msg = getSpeakString("default " + prefix + key);
+ if (msg != "")
+ return msg;
+ }
}
- return (false);
+ // No prefixes
+ msg = getSpeakString("default " + key);
+
+ return msg;
}
-static bool say_specific_dialogue(const monsters *monster,
- const mon_dialogue *dialogue)
+// Player ghosts with different classes can potentially speak different
+// things.
+static std::string player_ghost_speak_str(const monsters *monster,
+ const std::vector<std::string> prefixes)
{
- if (silenced(monster->x, monster->y))
- return (say_dialogue(monster, dialogue->silenced));
-
- if (monster->has_ench(ENCH_CHARM))
- return (false);
+ const ghost_demon &ghost = *(monster->ghost);
+ std::string ghost_class = get_class_name(ghost.values[GVAL_CLASS]);
- const bool friendly = (monster->attitude == ATT_FRIENDLY);
+ std::string prefix = "";
+ for (int i = 0, size = prefixes.size(); i < size; i++)
+ {
+ prefix += prefixes[i];
+ prefix += " ";
+ }
- if (mons_is_confused(monster))
- return (say_dialogue(
- monster,
- friendly? dialogue->confused_friend
- : dialogue->confused));
+ std::string msg = getSpeakString(prefix + ghost_class + " player ghost");
- if (monster->behaviour == BEH_FLEE)
- return (say_dialogue(
- monster,
- friendly? dialogue->fleeing_friend
- : dialogue->fleeing));
+ if (msg == "__NONE")
+ return "";
- if (monster->attitude == ATT_FRIENDLY)
- return (say_dialogue(monster, dialogue->friendly));
+ if (msg == "" || msg == "__NEXT")
+ msg = getSpeakString(prefix + "player ghost");
- return (say_dialogue(monster, dialogue->hostile));
+ return msg;
}
-// returns true if something is said
+ // returns true if something is said
bool mons_speaks(const monsters *monster)
{
- int temp_rand; // probability determination
-
- // This function is a little bit of a problem for the message channels
- // since some of the messages it generates are "fake" warning to
- // scare the player. In order to accomidate this intent, we're
- // falsely categorizing various things in the function as spells and
- // danger warning... everything else just goes into the talk channel -- bwr
- msg_channel_type msg_type = MSGCH_TALK;
-
- const std::string m_name = monster->name(DESC_CAP_THE);
- strcpy(info, m_name.c_str());
+ // Invisible monster tries to remain unnoticed. Unless they're
+ // confused, since then they're too confused to realize they
+ // should stay silent, but only if the player can see them, so as
+ // to not have to deal with cases of speaking monsters which the
+ // player can't see.
+ if (monster->invisible() && !(player_monster_visible(monster)
+ && monster->has_ench(ENCH_CONFUSION)))
+ return false;
- if (monster->invisible())
+ // Dealing with the monster not being silenced while the player
+ // *is* silenced, and is hence able to see the monsters' gestures
+ // and such but not hear any sounds it makes, would be a big
+ // headache to deal with, so skip it.
+ if (!silenced(monster->x, monster->y)
+ && silenced(you.x_pos, you.y_pos))
return false;
- // invisible monster tries to remain unnoticed
- const mon_dialogue *dialogue = find_dialogue(monster);
- if (dialogue)
- return (say_specific_dialogue(monster, dialogue));
-
- //mv: if it's also invisible, program never gets here
- if (silenced(monster->x, monster->y))
- {
+ // Silenced monsters only "speak" 1/3 as often as non-silenced,
+ // unless they're normally silent (S_SILENT). Use
+ // get_monster_data(monster->type) to bypass mon_shouts()
+ // replacing S_RANDOM with a random value.
+ if (silenced(monster->x, monster->y)
+ && get_monster_data(monster->type)->shouts != S_SILENT)
if (!one_chance_in(3))
- return false; // while silenced, don't bother so often
-
- if (monster->has_ench(ENCH_CONFUSION))
- {
- temp_rand = random2(10);
- strcat(info, (temp_rand < 4) ? " gestures wildly." :
- (temp_rand == 4) ? " looks confused." :
- (temp_rand == 5) ? " grins evilly." :
- (temp_rand == 6) ? " smiles happily." :
- (temp_rand == 7) ? " cries."
- : " says something but you don't hear anything.");
- }
- else if (monster->behaviour == BEH_FLEE)
- {
- temp_rand = random2(10);
- strcat(info,
- (temp_rand < 3) ? " glances furtively about." :
- (temp_rand == 3) ? " opens its mouth, as if shouting." :
- (temp_rand == 4) ? " looks around." :
- (temp_rand == 5) ? " appears indecisive." :
- (temp_rand == 6) ? " ponders the situation."
- : " seems to say something.");
- }
- // disregard charmed critters.. they're not too expressive
- else if (monster->attitude == ATT_FRIENDLY)
- {
- temp_rand = random2(10);
- strcat(info, (temp_rand < 3) ? " gives you a thumbs up." :
- (temp_rand == 3) ? " looks at you." :
- (temp_rand == 4) ? " waves at you." :
- (temp_rand == 5) ? " smiles happily.":
- (temp_rand == 6) ? " winks at you."
- : " says something you can't hear.");
- }
- else
- {
- temp_rand = random2(10);
- strcat(info, (temp_rand < 3) ? " gestures." :
- (temp_rand == 3) ? " gestures obscenely." :
- (temp_rand == 4) ? " grins." :
- (temp_rand == 5) ? " looks angry." :
- (temp_rand == 6) ? " seems to be listening."
- : " says something but you don't hear anything.");
- } //end switch silenced monster's behaviour
-
- mpr(info, MSGCH_TALK);
- return true;
- } // end silenced monster
+ return false;
// charmed monsters aren't too expressive
if (monster->has_ench(ENCH_CHARM))
@@ -227,2397 +169,216 @@ bool mons_speaks(const monsters *monster)
// berserk monsters just want your hide.
if (monster->has_ench(ENCH_BERSERK))
return false;
-
+
+ std::vector<std::string> prefixes;
+ if (monster->attitude == ATT_FRIENDLY)
+ prefixes.push_back("friendly");
+
+ if (monster->behaviour == BEH_FLEE)
+ prefixes.push_back("fleeing");
+
+ if (silenced(monster->x, monster->y))
+ prefixes.push_back("silenced");
+
if (monster->has_ench(ENCH_CONFUSION))
- {
- if (mons_holiness( monster ) == MH_DEMONIC
- && monster->type != MONS_IMP)
- {
- return (false);
- }
+ prefixes.push_back("confused");
+
+ std::string msg;
+
+ // __NONE means to be silent, and __NEXT means to try the next,
+ // less exact method of describing the monster to find a speech
+ // string.
+
+ // First, try its exact name
+ if (monster->type == MONS_PLAYER_GHOST)
+ // Player ghosts are treated differently.
+ msg = player_ghost_speak_str(monster, prefixes);
+ else if (monster->type == MONS_PANDEMONIUM_DEMON)
+ // Pandemonium demons have randomly generated names,
+ // so use "pandemonium lord" instead.
+ msg = get_speak_string(prefixes, "pandemonium lord", monster);
+ else
+ msg = get_speak_string(prefixes, monster->name(DESC_PLAIN), monster);
- if (mons_friendly(monster))
- {
- switch (random2(18)) // speaks for friendly confused monsters
- {
- case 0:
- strcat(info, " prays for help.");
- break;
- case 1:
- strcat(info, " screams, \"Help!\"");
- break;
- case 2:
- strcat(info, " shouts, \"I'm losing control!\"");
- break;
- case 3:
- strcat(info, " shouts, \"What's happening?\"");
- break;
- case 4:
- case 5:
- strcat(info, " gestures wildly.");
- break;
- case 6:
- strcat(info, " cries.");
- break;
- case 7:
- strcat(info, " shouts, \"Yeah!\"");
- break;
- case 8:
- strcat(info, " sings.");
- break;
- case 9:
- strcat(info, " laughs crazily.");
- break;
- case 10:
- strcat(info, " ponders the situation.");
- break;
- case 11:
- strcat(info, " grins madly.");
- break;
- case 12:
- strcat(info, " looks very confused.");
- break;
- case 13:
- strcat(info, " mumbles something.");
- break;
- case 14:
- strcat(info, " giggles crazily.");
- break;
- case 15:
- strcat(info, " screams, \"");
- strcat(info, you.your_name);
- strcat(info, "! Help!\"");
- break;
- case 16:
- strcat(info, " screams, \"");
- strcat(info, you.your_name);
- strcat(info, "! What's going on?\"");
- break;
- case 17:
- strcat(info, " says, \"");
- strcat(info, you.your_name);
- strcat(info, ", I'm a little confused.\"");
- break;
- }
- }
- else
- {
- switch (random2(23)) // speaks for unfriendly confused monsters
- {
- case 0:
- strcat(info, " yells, \"Get them off me!\"");
- break;
- case 1:
- strcat(info, " screams, \"I will kill you anyway!\"");
- break;
- case 2:
- strcat(info, " shouts, \"What's happening?\"");
- break;
- case 3:
- case 4:
- case 5:
- strcat(info, " gestures wildly.");
- break;
- case 6:
- strcat(info, " cries.");
- break;
- case 7:
- strcat(info, " shouts, \"NO!\"");
- break;
- case 8:
- strcat(info, " shouts, \"YES!\"");
- break;
- case 9:
- strcat(info, " laughs crazily.");
- break;
- case 10:
- strcat(info, " ponders the situation.");
- break;
- case 11:
- strcat(info, " grins madly.");
- break;
- case 12:
- strcat(info, " looks very confused.");
- break;
- case 13:
- strcat(info, " mumbles something.");
- break;
- case 14:
- strcat(info, " says, \"I'm a little confused.\"");
- break;
- case 15:
- strcat(info, " asks, \"Where am I?\"");
- break;
- case 16:
- strcat(info, " shakes.");
- break;
- case 17:
- strcat(info, " asks, \"Who are you?\"");
- break;
- case 18:
- strcat(info, " asks, \"What the hell are we doing here? Mmm, I see...\"");
- break;
- case 19:
- strcat(info, " cries, \"My head! MY HEAD!!!\"");
- break;
- case 20:
- strcat(info, " says, \"Why is everything spinning?\"");
- break;
- case 21:
- strcat(info, " screams, \"NO! I can't bear that much noise!\"");
- break;
- case 22:
- strcat(info, " is trying to cover his eyes.");
- break;
- }
- }
+ if (msg == "__NONE")
+ return false;
- }
- else if (monster->has_ench(ENCH_HELD))
+ // Now that we're not dealing with a specific monster name, include
+ // whether or not it can move in the prefix
+ if (mons_is_stationary(monster))
+ prefixes.insert(prefixes.begin(), "stationary");
+
+ // Names for the monster, its species and its genus all failed,
+ // so try the monster's glyph/symbol.
+ if (msg == "" || msg == "__NEXT")
{
- if (mons_friendly(monster))
- {
- switch(random2(8))
- {
- case 0:
- strcat(info, " says, \"Help me, ");
- strcat(info, you.your_name);
- strcat(info, ", please!\"");
- break;
- case 1:
- strcat(info, " cries, \"MUMMY!\"");
- break;
- case 2:
- strcat(info, " shouts, \"");
- strcat(info, you.your_name);
- strcat(info, "! Can't you see I need your help?\"");
- break;
- case 3:
- strcat(info, " shouts, \"I could do with a little help here, you know.\"");
- break;
- case 4:
- strcat(info, " mumbles something.");
- break;
- case 5:
- strcat(info, " says, \"Umm, ");
- strcat(info, you.your_name);
- strcat(info, "? Help?\"");
- break;
- case 6:
- strcat(info, " cries.");
- break;
- case 7:
- strcat(info, " cries, \"Why me?");
- break;
- }
- }
- else // unfriendly monsters
- {
- switch(random2(12))
- {
- case 0:
- strcat(info, " screams, \"HEY! This isn't fair!\"");
- break;
- case 1:
- strcat(info, " screams, \"Help! Get me out of here!\"");
- break;
- case 2:
- strcat(info, " begs, \"Could you help me? I swear I won't hurt you.\"");
- break;
- case 3:
- strcat(info, " yells, \"LEMME GO!\"");
- break;
- case 4:
- strcat(info, " cries, \"Please! I'll never do it again!\"");
- break;
- case 5:
- strcat(info, " mutters, \"Just what did I do to deserve this?\"");
- break;
- case 6:
- strcat(info, " asks, \"Hey, want to switch places?\"");
- break;
- case 7:
- strcat(info, " cries, \"I hate you!\"");
- break;
- case 8:
- strcat(info, " snarls, \"This is all your fault!\"");
- break;
- case 9:
- strcat(info, " says, \"I meant to do this, just so you know.\"");
- break;
- case 10:
- strcat(info, " shouts, \"This is all a huge misunderstanding!");
- break;
- case 11:
- strcat(info, " cries, \"Why me?\"");
- break;
- }
- }
+ std::string key = "'";
+
+ // Database keys are case-insensitve.
+ if (isupper(mons_char(monster->type)))
+ key += "cap-";
+
+ key += mons_char(monster->type);
+ key += "'";
+ msg = get_speak_string(prefixes, key, monster);
}
- else if (monster->behaviour == BEH_FLEE)
- {
- if (mons_holiness( monster ) == MH_DEMONIC
- && monster->type != MONS_IMP)
- {
- return (false);
- }
+ if (msg == "__NONE")
+ return false;
- if (mons_friendly(monster))
+ // Monster symbol didn't work, try monster shape. Since we're
+ // dealing with just the monster shape, change the prefix to
+ // include info on if the monster's intelligence is at odds with
+ // its shape.
+ mon_body_shape shape = get_mon_shape(monster);
+ mon_intel_type intel = mons_intel(monster->type);
+ if (shape >= MON_SHAPE_HUMANOID && shape <= MON_SHAPE_NAGA
+ && intel < I_NORMAL)
+ prefixes.insert(prefixes.begin(), "stupid");
+ else if (shape >= MON_SHAPE_QUADRUPED && shape <= MON_SHAPE_FISH)
+ {
+ if (mons_char(monster->type) == 'w')
{
- switch (random2(11))
- {
- case 0:
- snprintf( info, INFO_SIZE, "%s %s, \"WAIT FOR ME!\"",
- m_name.c_str(), coinflip() ? "shouts" : "yells");
- strcat(info, you.your_name);
- strcat(info, ", could you help me?\"");
- break;
- case 1:
- strcat(info, " screams, \"Help!\"");
- break;
- case 2:
- strcat(info, " shouts, \"Cover me!\"");
- break;
- case 3:
- strcat(info, " screams, \"");
- strcat(info, you.your_name);
- strcat(info, "! Help me!\"");
- break;
- case 4:
- case 5:
- case 6:
- strcat(info, " tries to hide somewhere.");
- break;
- case 7:
- strcat(info, " prays for help.");
- break;
- case 8:
- strcat(info, " looks at you beseechingly.");
- break;
- case 9:
- strcat(info, " shouts, \"Protect me!\"");
- break;
- case 10:
- strcat(info, " cries, \"Don't forget your friends!\"");
- break;
- }
+ if (intel > I_INSECT)
+ prefixes.insert(prefixes.begin(), "smart");
+ else if (intel < I_INSECT)
+ prefixes.insert(prefixes.begin(), "stupid");
}
else
{
- switch (random2(20)) // speaks for unfriendly fleeing monsters
- {
- case 0:
- snprintf( info, INFO_SIZE, "%s %s, \"Help!\"", m_name.c_str(),
- coinflip()? "yells" : "wails");
- break;
- case 1:
- snprintf( info, INFO_SIZE, "%s %s, \"Help!\"", m_name.c_str(),
- coinflip() ? "cries" : "screams"); break;
- case 2:
- snprintf( info, INFO_SIZE, "%s %s, \"Why can't we all just get along?\"",
- m_name.c_str(), coinflip() ? "begs" : "pleads");
- break;
- case 3:
- snprintf( info, INFO_SIZE, "%s %s trips in trying to escape.", m_name.c_str(),
- coinflip() ? "nearly" : "almost");
- break;
- case 4:
- snprintf( info, INFO_SIZE, "%s %s, \"Of all the rotten luck!\"", m_name.c_str(),
- coinflip() ? "mutters" : "mumbles");
- break;
- case 5:
- snprintf( info, INFO_SIZE, "%s %s, \"Oh dear! Oh dear!\"", m_name.c_str(),
- coinflip() ? "moans" : "wails");
- case 6:
- snprintf( info, INFO_SIZE, "%s %s, \"Damn and blast!\"", m_name.c_str(),
- coinflip() ? "mutters" : "mumbles");
- break;
- case 7:
- strcat(info, " prays for help.");
- break;
- case 8:
- strcat(info, " shouts, \"No! I'll never do that again!\"");
- break;
- case 9:
- snprintf( info, INFO_SIZE, "%s %s", m_name.c_str(),
- coinflip() ? "begs for mercy." : "cries, \"Mercy!\"");
- break;
- case 10:
- snprintf( info, INFO_SIZE, "%s %s, \"%s!\"", m_name.c_str(),
- coinflip() ? "blubbers" : "cries",
- coinflip() ? "Mommeee" : "Daddeee");
- break;
- case 11:
- snprintf( info, INFO_SIZE, "%s %s, \"Please don't kill me!\"", m_name.c_str(),
- coinflip() ? "begs" : "pleads");
- break;
- case 12:
- snprintf( info, INFO_SIZE, "%s %s, \"Please don't hurt me!\"", m_name.c_str(),
- coinflip() ? "begs" : "pleads");
- break;
- case 13:
- snprintf( info, INFO_SIZE, "%s %s, \"Please, I have a lot of children...\"",
- m_name.c_str(), coinflip() ? "begs" : "pleads");
- break;
- case 14:
- strcat(info, " tries to recover lost courage.");
- break;
- case 15:
- case 16:
- case 17:
- strcat(info, " gives up.");
- break;
- case 19:
- snprintf( info, INFO_SIZE, "%s looks really %s.",
- m_name.c_str(),
- coinflip() ? "scared stiff" : "rattled");
- break;
- }
+ if (intel > I_ANIMAL)
+ prefixes.insert(prefixes.begin(), "smart");
+ else if (intel < I_ANIMAL)
+ prefixes.insert(prefixes.begin(), "stupid");
}
}
- else if (mons_friendly(monster))
+ else if (shape >= MON_SHAPE_INSECT && shape <= MON_SHAPE_SNAIL)
{
- if (mons_holiness( monster ) == MH_DEMONIC
- && monster->type != MONS_IMP)
- {
- return (false);
- }
-
- // friendly imps are too common so they speak very very rarely
- if (monster->type == MONS_IMP)
- {
- if (!one_chance_in(10))
- return (false);
-
- switch (random2(12))
- {
- case 0:
- strcat(info, " says, \"Just tell me who NOT to kill.\"");
- break;
- case 1:
- strcat(info, " says, \"OK Boss!\"");
- break;
- case 2:
- strcat(info, " grins impishly at you.");
- break;
- case 3:
- strcat(info, " picks up some beetles from the floor "
- "and offers them to you.");
- break;
- case 4:
- strcat(info, " blows smoke rings.");
- break;
- case 5:
- strcat(info, " shouts, \"Over here! I found it!\"");
- break;
- case 6:
- strcat(info, " says, \"The Orb is all yours.\"");
- break;
- case 7:
- strcat(info, " says, \"Isn't this more fun with friends?\"");
- break;
- case 8:
- strcat(info, " says, \"Uh-oh! Wait. OK.\"");
- break;
- case 9:
- strcat(info, " shouts, \"Stay back! It could be a trick.\"");
- break;
- case 10:
- strcat(info, " says, \"You're so much nicer than "
- "my last boss.\"");
- break;
- case 11:
- strcat(info, " jumps up and down with excitement.\"");
- break;
- }
- }
-
- else
- {
- switch (random2(18))
- {
- case 0:
- strcat(info, " yells, \"Run! I'll cover you!\"");
- break;
- case 1:
- strcat(info, " shouts, \"Die, monster!\"");
- break;
- case 2:
- strcat(info, " says, \"It's nice to have friends.\"");
- break;
+ if (intel > I_INSECT)
+ prefixes.insert(prefixes.begin(), "smart");
+ else if (intel < I_INSECT)
+ prefixes.insert(prefixes.begin(), "stupid");
+ }
+ else if (shape >= MON_SHAPE_PLANT && shape <= MON_SHAPE_BLOB
+ && intel > I_PLANT)
+ prefixes.insert(prefixes.begin(), "smart");
- case 3:
- strcat(info, " looks at you.");
- break;
- case 4:
- strcat(info, " smiles at you.");
- break;
- case 5:
- strcat(info, " says, \"");
- strcat(info, you.your_name);
- strcat(info, ", you are my only friend.\"");
- break;
- case 6:
- strcat(info, " says, \"");
- strcat(info, you.your_name);
- strcat(info, ", I like you.\"");
- break;
+ if (msg == "" || msg == "__NEXT")
+ msg = get_speak_string(prefixes, get_mon_shape_str(shape), monster);
+ if (msg == "__NONE")
+ return false;
- case 7:
- strcat(info, " waves at you.");
- break;
- case 8:
- strcat(info, " says, \"Be careful!\"");
- break;
- case 9:
- strcat(info, " says, \"Don't worry. I'm here with you.\"");
- break;
- case 10:
- strcat(info, " smiles happily.");
- break;
- case 11:
- strcat(info, " shouts, \"No mercy! Kill them all!");
- break;
- case 12:
- strcat(info, " winks at you.");
- break;
- case 13:
- strcat(info, " says, \"Me and you. It sounds cool.\"");
- break;
- case 14:
- strcat(info, " says, \"I'll never leave you.\"");
- break;
- case 15:
- strcat(info, " says, \"I would die for you.\"");
- break;
- case 16:
- strcat(info, " shouts, \"Beware of monsters!\"");
- break;
- case 17:
- strcat(info, " looks friendly.");
- break;
- }
- }
- }
- else
+ // If we failed to get a message with a winged or tailed humanoid,
+ // or a naga or centaur, try moving closer to plain humanoid
+ if ((msg == "" || msg == "__NEXT") && shape > MON_SHAPE_HUMANOID
+ && shape <= MON_SHAPE_NAGA)
{
- switch (monster->type)
+ // If a humanoid monster has both wings and a tail, try
+ // removing one and then the other to see if we get any
+ // results.
+ if (shape == MON_SHAPE_HUMANOID_WINGED_TAILED)
{
- case MONS_TERENCE: // fighter who likes to kill
- switch (random2(15))
- {
- case 0:
- strcat(info, " screams, \"I'm going to kill you! \"");
- break;
- case 1:
- strcat(info, " shouts, \"Now you die.\"");
- break;
- case 2:
- strcat(info, " says, \"Rest in peace.\"");
- break;
- case 3:
- snprintf( info, INFO_SIZE, "%s shouts, \"%s!!!\"",
- m_name.c_str(), coinflip() ? "ATTACK" : "DIE");
- break;
- case 4:
- strcat(info, " says, \"How do you enjoy it?\"");
- break;
- case 5:
- strcat(info, " shouts, \"Get ready for death!\"");
- break;
- case 6:
- strcat(info, " says, \"You are history.\"");
- break;
- case 7:
- strcat(info, " says, \"Do you want it fast or slow?.\"");
- break;
- case 8:
- strcat(info, " says, \"Did you write a testament? You should...\"");
- break;
- case 9:
- strcat(info, " says, \"Time to say good-bye...\"");
- break;
- case 10:
- snprintf( info, INFO_SIZE, "%s says, \"Don't try to defend, it's %s.\"",
- m_name.c_str(), coinflip() ? "pointless" : "senseless");
- break;
- case 11:
- strcat(info, " bares his teeth.");
- break;
- case 12:
- snprintf( info, INFO_SIZE, "%s says, \"I'll show you few %s.\"",
- m_name.c_str(), coinflip() ? "tricks" : "ploys.");
- break;
- case 13:
- strcat(info, " screams, \"I want your blood.\"");
- break;
- case 14:
- strcat(info, " looks scornfully at you.");
- break;
- }
- break; // end Terence
+ shape = MON_SHAPE_HUMANOID_TAILED;
+ msg = get_speak_string(prefixes,
+ get_mon_shape_str(shape),
+ monster);
- case MONS_EDMUND: // mercenaries guarding dungeon
- case MONS_LOUISE:
- case MONS_FRANCES:
- case MONS_DUANE:
- case MONS_FREDERICK:
- switch (random2(17))
+ // Only be silent if both tailed and winged return __NONE
+ if (msg == "" || msg == "__NONE" || msg == "__NEXT")
{
- case 0:
- strcat(info, " screams, \"I'm going to kill you! Now!\"");
- break;
- case 1:
- strcat(info,
- " shouts, \"Return immediately or I'll kill you!\"");
- break;
- case 2:
- strcat(info,
- " says, \"Now you've reached the end of your journey!\"");
- break;
- case 3:
- strcat(info,
- " screams, \"One false step and I'll kill you!\"");
- break;
- case 4:
- strcat(info, " says, \"Drop everything you've found here and return home.\"");
- break;
- case 5:
- strcat(info, " shouts, \"You will never get the Orb.\"");
- break;
- case 6:
- strcat(info, " looks very unfriendly.");
- break;
- case 7:
- strcat(info, " looks very cold.");
- break;
- case 8:
- strcat(info, " shouts, \"It's the end of the party!\"");
- break;
- case 9:
- strcat(info, " says, \"Return every stolen item!\"");
- break;
- case 10:
- strcat(info, " says, \"No trespassing is allowed here.\"");
- break;
- case 11:
- strcat(info, " grins evilly.");
- break;
- case 12:
- strcat(info, " screams, \"You must be punished!\"");
- break;
- case 13:
- strcat(info, " says, \"It's nothing personal...\"");
- break;
- case 14:
- strcat(info, " says, \"A dead adventurer is a good adventurer.\"");
- break;
- case 15:
- strcat(info, " says, \"Coming here was your last mistake.\"");
- break;
- case 16:
- strcat(info, " shouts, \"Intruder!\"");
- break;
- }
- break; // end Edmund & Co
+ shape = MON_SHAPE_HUMANOID_WINGED;
+ std::string msg2;
+ msg2 = get_speak_string(prefixes,
+ get_mon_shape_str(shape),
+ monster);
+ if (msg == "__NONE" && msg2 == "__NONE")
+ return false;
- case MONS_JOSEPH:
- switch (random2(16))
- {
- case 0:
- strcat(info, " smiles happily.");
- break;
- case 1:
- strcat(info, " says, \"I'm happy to see you. And I'll be happy to kill you.\"");
- break;
- case 2:
- strcat(info, " says, \"I've waited for this moment for such a long time.\"");
- break;
- case 3:
- strcat(info,
- " says, \"It's nothing personal, but I have to kill you.\"");
- break;
- case 5:
- strcat(info, " says, \"You will never get the Orb, sorry.\"");
- break;
- case 9:
- strcat(info, " shouts, \"I love to fight! I love killing!\"");
- break;
- case 10:
- strcat(info,
- " says, \"I'm here to kill trespassers. I like my job.\"");
- break;
- case 11:
- strcat(info, " tries to grin evilly.");
- break;
- case 12:
- strcat(info,
- " says, \"You must be punished! Or... I want to punish you!\"");
- break;
- case 13:
- strcat(info,
- " sighs, \"Being a guard is usually so boring...\"");
- break;
- case 14:
- strcat(info, " shouts, \"At last some action!\"");
- break;
- case 15:
- strcat(info, " shouts, \"Wow!\"");
- break;
- }
- break; // end Joseph
+ if (msg2 == "__NONE")
+ msg2 = "";
- case MONS_ORC_HIGH_PRIEST: // priest, servants of dark ancient god
- case MONS_DEEP_ELF_HIGH_PRIEST:
- switch (random2(12))
- {
- case 0:
- case 1:
- strcat(info, " prays.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
-
- case 2:
- strcat(info, " mumbles some strange prayers.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
-
- case 3:
- strcat(info,
- " shouts, \"You are a heretic and must be destroyed.\"");
- break;
- case 4:
- strcat(info, " says, \"All sinners must die.\"");
- break;
-
- case 5:
- strcat(info, " looks excited.");
- break;
- case 6:
- strcat(info, " says, \"You will make a fine sacrifice.\"");
- break;
- case 7:
- strcat(info, " starts to sing a prayer.");
- break;
- case 8:
- strcat(info, " shouts, \"You must be punished.\"");
- break;
- case 9:
- strcat(info, " intones a terrible prayer.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
- case 10:
- strcat(info, " says, \" Right in the middle of my "
- "sermon, too.\"");
- break;
- case 11:
- strcat(info, " says, \"The wages of sin are death, "
- "you know.\"");
- break;
+ msg = msg2;
}
- break; // end priests
-
- case MONS_ORC_SORCERER: // hateful wizards, using strange powers
- case MONS_DEEP_ELF_SORCERER:
- case MONS_WIZARD:
- switch (random2(19))
- {
- case 0:
- case 1:
- case 2:
- strcat(info, " wildly gestures.");
- mpr( info, MSGCH_MONSTER_SPELL );
- if (coinflip())
- canned_msg( MSG_NOTHING_HAPPENS );
- else
- canned_msg( MSG_YOU_RESIST );
- return (true);
-
- case 3:
- case 4:
- case 5:
- strcat(info, " mumbles some strange words.");
- mpr( info, MSGCH_MONSTER_SPELL );
- if (coinflip())
- canned_msg( MSG_NOTHING_HAPPENS );
- else
- canned_msg( MSG_YOU_RESIST );
- return (true);
-
- case 6:
- strcat(info, " shouts, \"You can't withstand my power!\"");
- break;
+ } // if (shape == MON_SHAPE_HUMANOID_WINGED_TAILED)
+ if (msg == "" || msg == "__NONE" || msg == "__NEXT")
+ {
+ shape = MON_SHAPE_HUMANOID;
+ msg = get_speak_string(prefixes,
+ get_mon_shape_str(shape),
+ monster);
+ }
+ }
+ if (msg == "__NONE" || msg == "")
+ return false;
- case 7:
- strcat(info, " shouts, \"You are history.\"");
- break;
+ if (msg == "__NEXT")
+ {
+ msg::streams(MSGCH_DIAGNOSTICS)
+ << "__NEXT used by shape-based speech string for monster '"
+ << monster->name(DESC_PLAIN) << "'" << std::endl;
+ return false;
+ }
- case 8:
- simple_monster_message( monster, " casts a spell.",
- MSGCH_MONSTER_SPELL );
+ // We have a speech string, now parse and act on it.
+ msg = do_mon_str_replacements(msg, monster);
- strcat(info, " becomes transparent for a moment.");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
+ std::vector<std::string> lines = split_string("\n", msg);
- case 9:
- strcat(info, " throws some strange powder towards you.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
+ for (int i = 0, size = lines.size(); i < size; i++)
+ {
+ std::string line = lines[i];
- case 10:
- simple_monster_message( monster, " casts a spell.",
- MSGCH_MONSTER_SPELL );
+ if (line == "__YOU_RESIST")
+ {
+ canned_msg( MSG_YOU_RESIST );
+ continue;
+ }
+ else if (line == "__NOTHING_HAPPENS")
+ {
+ canned_msg( MSG_NOTHING_HAPPENS );
+ continue;
+ }
+ else if (line == "__MORE")
+ {
+ more();
+ continue;
+ }
- strcat(info, " glows brightly for a moment.");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
+ // This function is a little bit of a problem for the message
+ // channels since some of the messages it generates are "fake"
+ // warning to scare the player. In order to accomidate this
+ // intent, we're falsely categorizing various things in the
+ // function as spells and danger warning... everything else
+ // just goes into the talk channel -- bwr
+ msg_channel_type msg_type = MSGCH_TALK;
- case 11:
- strcat(info, " says, \"argatax netranoch dertex\"");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
+ std::string::size_type pos = line.find(":");
- case 12:
- strcat(info, " says, \"dogrw nutew berg\"");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
-
- case 13:
- strcat(info, " shouts, \"Entram moth deg ulag!\"");
+ if (pos != std::string::npos)
+ {
+ std::string param = line.substr(0, pos);
+ bool match = true;
+
+ if (param == "DANGER")
+ msg_type = MSGCH_DANGER;
+ else if (param == "WARN")
+ msg_type = MSGCH_WARN;
+ else if (param == "SOUND")
+ msg_type = MSGCH_SOUND;
+ else if (param == "SPELL")
msg_type = MSGCH_MONSTER_SPELL;
- break;
-
- case 14:
- strcat(info, " casts a spell.");
- mpr(info, MSGCH_MONSTER_SPELL);
-
- strcpy(info, m_name.c_str());
- strcat(info, " becomes larger for a moment.");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
-
- case 15:
- strcat(info, " casts a spell.");
- mpr(info, MSGCH_MONSTER_SPELL);
-
- strcpy(info, m_name.c_str());
- strcat(info, "'s fingertips start to glow.");
+ else if (param == "ENCHANT")
msg_type = MSGCH_MONSTER_ENCHANT;
- break;
-
- case 16:
- strcat(info, "'s eyes start to glow.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
-
- case 17:
- strcat(info, " tries to paralyze you with his gaze.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
-
- case 18:
- strcat(info, " casts a spell.");
- mpr(info, MSGCH_MONSTER_SPELL);
- canned_msg( MSG_YOU_RESIST );
- return (true);
- }
- break; // end wizards
-
- case MONS_JESSICA: // sorceress disturbed by player
- switch (random2(10))
- {
- case 0:
- strcat(info, " grins evilly.");
- break;
- case 1:
- strcat(info, " says, \"I'm really upset.\"");
- break;
- case 2:
- strcat(info, " shouts, \"I don't like beings like you.\"");
- break;
- case 3:
- strcat(info,
- " shouts, \"Stop bothering me, or I'll kill you!\"");
- break;
- case 4:
- strcat(info, " very coldly says, \"I hate your company.\"");
- break;
- case 5:
- strcat(info, " mumbles something strange.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
- case 6:
- strcat(info, " looks very angry.");
- break;
- case 7:
- strcat(info,
- " shouts, \"You're disturbing me. I'll have to kill you.\"");
- break;
- case 8:
- strcat(info, " screams, \"You are a ghastly nuisance!\"");
- break;
- case 9:
- strcat(info, " gestures wildly.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
- }
- break; // end Jessica
-
- case MONS_SIGMUND: // mad old wizard
- switch (random2(19))
- {
- case 0:
- case 1:
- case 2:
- strcat(info, " laughs crazily.");
- break;
- case 3:
- strcat(info, " says, \"Don't worry, I'll kill you fast.\"");
- break;
- case 4:
- strcat(info, " grinds his teeth.");
- break;
- case 5:
- strcat(info, " asks, \"Do you like me?\"");
- break;
- case 6:
- strcat(info, " screams, \"Die, monster!\"");
- break;
- case 7:
- strcat(info, " says, \"You will soon forget everything.\"");
- break;
- case 8:
- strcat(info, " screams, \"You will never... NEVER!\"");
- break;
-
- case 9:
- simple_monster_message( monster, " casts a spell.",
- MSGCH_MONSTER_SPELL );
-
- strcat(info, "'s eyes start to glow with a red light. ");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
-
- case 10:
- strcat(info, " says, \"Look into my eyes.\"");
- break;
- case 11:
- strcat(info, " says, \"I'm your fate.\"");
- break;
-
- case 12:
- simple_monster_message( monster, " casts a spell.",
- MSGCH_MONSTER_SPELL );
-
- strcat(info, " is suddenly surrounded by pale blue light.");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
-
- case 13:
- strcat(info, " tries to bite you.");
- break;
-
- case 14:
- simple_monster_message( monster, " casts a spell.",
- MSGCH_MONSTER_SPELL );
-
- strcat(info, " is suddenly surrounded by pale green light.");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
-
- case 15:
- strcat(info, " screams, \"I am the angel of Death!\"");
- break;
- case 16:
- strcat(info, " screams, \"Only death can liberate you!\"");
- break;
- case 17:
- strcat(info, " whispers, \"You'll know eternity soon...\"");
- break;
- case 18:
- strcat(info, " screams, \"Don't try to resist!\"");
- break;
- }
- break; // end Sigmund
-
- case MONS_IMP: // small demon
- case MONS_WHITE_IMP:
- case MONS_SHADOW_IMP:
- if (one_chance_in(3))
- {
- imp_taunt( monster );
- return (true);
- }
+ else if (param == "PLAIN")
+ msg_type = MSGCH_PLAIN;
else
- {
- switch (random2(11))
- {
- case 0:
- strcat(info, " laughs crazily.");
- break;
- case 1:
- strcat(info, " grins evilly.");
- break;
- case 2:
- strcat(info, " breathes smoke at you.");
- break;
- case 3:
- strcat(info, " lashes his tail.");
- break;
- case 4:
- strcat(info, " grinds his teeth.");
- break;
- case 5:
- strcat(info, " sputters.");
- break;
- case 6:
- strcat(info, " breathes steam at you.");
- break;
- case 7:
- strcat(info, " spits at you.");
- break;
- case 8:
- strcat(info, " disappears for a moment.");
- break;
- case 9:
- strcat(info, " summons a swarm of flies.");
- break;
- case 10:
- strcat(info, " picks up a beetle and eats it.");
- break;
- }
- }
- break; // end imp
-
- case MONS_TORMENTOR: // cruel devil
- if (one_chance_in(10))
- {
- demon_taunt( monster );
- return (true);
- }
- else
- {
- switch (random2(18))
- {
- case 0:
- strcat(info, " laughs crazily.");
- break;
- case 1:
- strcat(info, " grins evilly.");
- break;
- case 2:
- strcat(info, " says, \"I am all your nightmares come true.\"");
- break;
- case 3:
- strcat(info, " says, \"I will show you what pain is.\"");
- break;
- case 4:
- strcat(info, " shouts, \"I'll tear you apart.\"");
- break;
- case 5:
- strcat(info,
- " says, \"You will wish to die when I get to you.\"");
- break;
- case 6:
- strcat(info, " says, \"I will drown you in your own blood.\"");
- break;
- case 7:
- strcat(info,
- " screams, \"You will die horribly!\"");
- break;
- case 8:
- strcat(info, " says, \"I will eat your liver.\"");
- break;
- case 9:
- strcat(info, " grins madly.");
- break;
- case 10:
- strcat(info, " shouts, \"Prepare for my thousand needles of pain!\"");
- break;
- case 11:
- strcat(info,
- " says, \"I know a thousand and one ways to kill you.\"");
- break;
- case 12:
- strcat(info,
- " says, \"I'll show you my torture chamber!\"");
- break;
- case 13:
- case 14:
- strcat(info,
- " says, \"I'll crush your bones, one by one.\"");
- break;
- case 15:
- strcat(info, " says, \"I know your fate. It's pain.\"");
- break;
- case 16:
- strcat(info, " says, \"Get ready! Throes await you.\"");
- break;
- case 17:
- strcat(info, " grins malevolently.");
- break;
- }
- }
- break; // end tormentor
-
- case MONS_PANDEMONIUM_DEMON: // named demons
- case MONS_GERYON:
- case MONS_ASMODEUS:
- case MONS_DISPATER:
- case MONS_ANTAEUS:
- case MONS_ERESHKIGAL:
- case MONS_MNOLEG:
- case MONS_LOM_LOBON:
- case MONS_CEREBOV:
- case MONS_GLOORX_VLOQ:
- demon_taunt( monster );
- return (true);
-
- case MONS_PLAYER_GHOST: // ghost of unsuccesful player
- switch (random2(29))
- {
- case 0:
- strcat(info, " laughs crazily.");
- break;
-
- case 1:
- strcat(info, " grins evilly.");
- break;
-
- case 2:
- strcat(info, " shouts, \"You will never get the ORB!\"");
- break;
-
- case 3: // mv: ghosts are usually wailing, aren't ?
- strcat(info, " says, \"I have seen your future. "
- "And it's all used up.\"");
- break;
- case 4:
- strcat(info, " makes a sound of rattling chains.");
- break;
-
- case 5:
- strcat(info, " says, \"They lied to you. "
- "The Dungeon just goes down and down forever.\"");
- break;
- case 6:
- strcat(info, " says, \"Do you think the gods "
- "will protect you?\"");
- break;
- case 7:
- strcat(info, " says, \"I was like you once.\"");
- break;
- case 8:
- strcat(info, " says, \"Very impressive. But it won't help. "
- "Nothing will.\"");
- break;
- case 9:
- strcat(info, " whispers, \"They're coming for you...\"");
- break;
- case 10:
- strcat(info, " says, \"What have you got "
- "that I didn't have?\"");
- break;
-
- case 11:
- strcat(info, " wails.");
- break;
-
- case 12:
- strcat(info, " stares at you.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("You feel cold.", MSGCH_WARN);
- return (true);
-
- case 13:
- strcat(info, " screams, \"You will join me soon!\"");
- break;
- case 14:
- strcat(info, " wails, \"To die, to sleep, no more.\"");
- break; //Hamlet
- case 15:
- strcat(info,
- " screams, \"You must not succeed where I failed.\"");
- break;
- case 16:
- strcat(info,
- " screams, \"I'll kill anyone who wants the ORB.\"");
- break;
- case 17:
- strcat(info, " whispers, \"Meet emptiness of death!\"");
- break;
- case 18:
- strcat(info, " whispers, \"Death is liberation.\"");
- break;
- case 19:
- strcat(info,
- " whispers, \"Everlasting silence awaits you.\"");
- break;
- case 20:
- strcat(info,
- " screams, \"Don't try to defend. You have no chance!\"");
- break;
- case 21:
- strcat(info,
- " whispers, \"Death doesn't hurt. What you feel is life.\"");
- break;
- case 22:
- strcat(info, " whispers, \"The ORB doesn't exist.\"");
- break;
- case 23:
- strcat(info, " wails, \"Death is your only future.\"");
- break;
- case 24:
- strcat(info, " says, \"The more you struggle now, "
- "the more you'll suffer later.\"");
- break;
- case 25:
- strcat(info, " whispers, \"Trust me. Just give in.\"");
- break;
- case 26:
- strcat(info, " says very slowly, \"There's no hope.\"");
- break;
- case 27:
- strcat(info, " lets out a mournful wail.");
- break;
- case 28:
- strcat(info, " keens inconsolably.");
- break;
- }
- break; // end players ghost
-
- case MONS_PSYCHE: // insane girl
- switch (random2(20))
- {
- case 0:
- strcat(info, " smiles happily.");
- break;
- case 1:
- strcat(info, " giggles crazily.");
- break;
- case 2:
- strcat(info, " cries.");
- break;
- case 3:
- strcat(info, " stares at you for a moment.");
- break;
- case 4:
- strcat(info, " sings.");
- break;
- case 5:
- strcat(info,
- " says, \"Please, could you die a little faster?\"");
- break;
- case 6:
- strcat(info,
- " says, \"I'm a bad girl. But I can't do anything about it.\"");
- break;
- case 7:
- strcat(info,
- " screams, \"YOU ARE VIOLATING AREA SECURITY!\"");
- break;
- case 8:
- strcat(info, " cries, \"I hate blood and violence.\"");
- break;
- case 9:
- strcat(info,
- " screams, \"Peace! Flowers! Freedom! Dead adventurers!\"");
- break;
- case 10:
- strcat(info,
- " says, \"I'm so lonely. Only corpses are my friends.\"");
- break;
- case 11:
- strcat(info, " cries, \"You've killed my pet.\"");
- break;
- case 12:
- strcat(info,
- " cries, \"You want to steal my orb collection?!\"");
- break;
- case 13:
- strcat(info, " sings a strange song.");
- break;
- case 14:
- strcat(info, " bursts into tears.");
- break;
- case 15:
- strcat(info, " sucks her thumb.");
- break;
- case 16:
- strcat(info,
- " whispers, \"Hold me, thrill me, kiss me, kill me.\"");
- break; //(c) U2 ?
- case 17:
- strcat(info, " says, \"I'll kill you and take you home.\"");
- break;
- case 18:
- strcat(info,
- " shouts, \"Well, maybe I'm nutty, but who cares?\"");
- break;
- case 19:
- strcat(info,
- " shouts, \"I hope that you are sorry for that.\"");
- break;
- }
- break; // end Psyche
-
- case MONS_DONALD: // adventurers hating competition
- case MONS_WAYNE:
- switch (random2(11))
- {
- case 0:
- strcat(info, " screams, \"Return home!\"");
- break;
- case 1:
- strcat(info, " screams, \"The Orb is mine!\"");
- break;
- case 2:
- strcat(info, " screams, \"Give me all your treasure!\"");
- break;
- case 3:
- strcat(info, " screams, \"You will never get the Orb!\"");
- break;
- case 4:
- strcat(info, " screams, \"I was here first!\"");
- break;
- case 5:
- strcat(info, " frowns.");
- break;
- case 6:
- strcat(info, " looks very upset.");
- break;
- case 7:
- strcat(info, " screams, \"Get away or die!\"");
- break;
- case 8:
- strcat(info, " screams, \"Die!\"");
- break;
- case 9:
- strcat(info, " screams, \"First you have to pass me!\"");
- break;
- case 10:
- strcat(info, " screams, \"I hate you!\"");
- break;
- }
- break; // end Donald
-
- case MONS_MICHAEL: // spellcaster who wanted to be alone
- switch (random2(11))
- {
- case 0:
- strcat(info, " looks very angry.");
- break;
- case 1:
- strcat(info, " frowns.");
- break;
- case 2:
- strcat(info, " screams, \"I want to be alone!\"");
- break;
- case 3:
- strcat(info, " says, \"You are really a nuisance.\"");
- break;
- case 4:
- strcat(info,
- " screams, \"I wanted to be alone. And you...\"");
- break;
- case 5:
- strcat(info, " screams, \"Get away! Or better yet, die!\"");
- break;
- case 6:
- strcat(info, " mumbles some strange words.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
-
- case 7:
- strcat(info, " points at you.");
- mpr(info, MSGCH_MONSTER_SPELL);
- canned_msg(MSG_YOU_RESIST);
- return (true);
-
- case 8:
- strcat(info, " shakes with wrath.");
- break;
- case 9:
- strcat(info, " drinks a potion.");
- break;
- case 10:
- strcat(info, " gestures wildly.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
- }
- break; // end Michael
-
- case MONS_ERICA: // wild tempered adventuress
- switch (random2(12))
- {
- case 0:
- strcat(info, " screams, \"Die!\"");
- break;
- case 1:
- strcat(info, " screams, \"Do you want it fast or slow?\"");
- break;
- case 2:
- strcat(info, " looks angry.");
- break;
- case 3:
- strcat(info, " drinks a potion.");
- break;
- case 4:
- strcat(info, " says, \"I'm so much better than you.\"");
- break;
- case 5:
- strcat(info,
- " says, \"Fast and perfect. Such is my way of killing.\"");
- break;
- case 6:
- strcat(info, " screams, \"Hurry! Death awaits!\"");
- break;
- case 7:
- strcat(info, " laughs wildly.");
- break;
- case 8:
- strcat(info, " screams, \"I'll never tell where it is!\"");
- break;
- case 9:
- strcat(info, " screams, \"You'll never get it!\"");
- break;
- case 10:
- strcat(info, " screams, \"Coming here was suicide!\"");
- break;
- case 11:
- strcat(info,
- " says, \"I love to fight, but killing is better.\"");
- break;
- }
- break; // end Erica
-
- case MONS_JOSEPHINE: // ugly old witch looking for somone to kill
- switch (random2(13))
- {
- case 0:
- case 1:
- case 2:
- strcat(info, " grins evilly.");
- break;
- case 3:
- case 4:
- strcat(info, " screams, \"I will kill you!\"");
- break;
- case 5:
- strcat(info, " grinds her teeth.");
- break;
- case 6:
- strcat(info, " grins malevolently.");
- break;
- case 7:
- strcat(info, " laughs insanely.");
- break;
- case 8:
- strcat(info, " screams, \"Die!\"");
- break;
- case 9:
- strcat(info,
- " screams, \"I have something special for you!\"");
- break;
- case 10:
- strcat(info,
- " screams, \"I'll use your head as decoration in my hut!\"");
- break;
- case 11:
- strcat(info, " says, \"I'll make a rug of your skin.\"");
- break;
- case 12:
- strcat(info, " says, \"How about some decapitation?\"");
- break;
- }
- break; // end Josephine
+ match = false;
- case MONS_HAROLD: // middle aged man, hired to kill you. He is in a hurry.
- switch (random2(11))
- {
- case 0:
- strcat(info, " looks nervous.");
- break;
- case 1:
- strcat(info, " screams, \"Hurry up!\"");
- break;
- case 2:
- strcat(info, " screams, \"Could you die faster?\"");
- break;
- case 3:
- strcat(info,
- " says, \"Stand still. I'm trying to kill you.\"");
- break;
- case 4:
- strcat(info, " screams, \"Die!\"");
- break;
- case 5:
- strcat(info, " says, \"I hope you die soon!\"");
- break;
- case 6:
- strcat(info,
- " says, \"Only a few hits and it's over.\".");
- break;
- case 7:
- strcat(info, " says, \"You know, I'm in a hurry.\"");
- break;
- case 8:
- strcat(info, " screams, \"I'll finish you soon!\"");
- break;
- case 9:
- strcat(info, " screams, \"Don't delay it.\"");
- break;
- case 11:
- strcat(info, " says, \"Mine is not to reason why. Mine's to do, yours to die.\"" );
- }
- break; // end Harold
-
- // skilled warrior looking for some fame. More deads = more fame
- case MONS_NORBERT:
- switch (random2(13))
- {
- case 0:
- strcat(info, " smiles happily.");
- break;
- case 1:
- strcat(info, " screams, \"Die, monster!\"");
- break;
- case 2:
- strcat(info, " screams, \"I'm a hero!\"");
- break;
- case 3:
- strcat(info, " shouts, \"YES! Another notch!\"");
- break;
- case 4:
- strcat(info, " says, \"A pity your head will make such an ugly trophy.\"");
- break;
- case 5:
- strcat(info,
- " screams, \"Pray, because you'll die soon!\"");
- break;
- case 6:
- strcat(info,
- " asks \"Did you write a will? You should.\".");
- break;
- case 7:
- strcat(info,
- " says, \"I love killing ugly monsters like you.\"");
- break;
- case 8:
- strcat(info, " screams, \"Blood and destruction!\"");
- break;
- case 9:
- strcat(info, " says, \"You know, it's an honour to die by my hand.\"");
- break;
- case 10:
- strcat(info, " shouts, \"Your time has come!\"");
- break;
- case 11:
- strcat(info,
- " says, \"I'm sorry but you don't have a chance.\"");
- break;
- case 12:
- strcat(info,
- " says, \"Another dead monster... It must be my lucky day!\"");
- break;
- }
- break; // end Norbert
-
- case MONS_JOZEF: // bounty hunter
- switch (random2(14))
- {
- case 0:
- strcat(info, " looks satisfied.");
- break;
- case 1:
- strcat(info, " screams, \"Die!\"");
- break;
- case 2:
- strcat(info, " screams, \"At last I found you!\"");
- break;
- case 3:
- strcat(info, " shouts, \"I'll get 500 for your head!\"");
- break;
- case 4:
- strcat(info,
- " says, \"You don't look worth it for that money.\"");
- break;
- case 5:
- strcat(info,
- " says, \"It's nothing personal. I'm paid for it!\"");
- break;
- case 6:
- strcat(info,
- " asks \"Did you write a testament? You should.\"");
- break;
- case 7:
- strcat(info, " says, \"You are ");
- strcat(info, you.your_name);
- strcat(info, ", aren't you?.\"");
- break;
- case 8:
- strcat(info, " says, \"I suppose that you are ");
- strcat(info, you.your_name);
- strcat(info, ". Sorry, if I'm wrong.\"");
- break;
- case 9:
- strcat(info, " says, \"One dead ");
- strcat(info, you.your_name);
- strcat(info, ", 500 gold pieces. It's in my contract.\"");
- break;
- case 10:
- strcat(info, " shouts, \"Your time has come!\"");
- break;
- case 11:
- strcat(info,
- " says, \"My job is sometimes very exciting. Sometimes...\"");
- break;
- case 12:
- strcat(info, " says, \"I think I deserve my money.\"");
- break;
- case 13:
- strcat(info,
- " screams, \"Die! I've got more contracts today.\"");
- break;
- }
- break; // end Jozef
-
- case MONS_AGNES: // she is trying to get money and treasure
- switch (random2(10))
- {
- case 0:
- strcat(info, " screams, \"Give me all your money!\"");
- break;
- case 1:
- strcat(info, " screams, \"All treasure is mine!\"");
- break;
- case 2:
- strcat(info, " screams, \"You'll never get my money!\"");
- break;
- case 3:
- strcat(info, " grins evilly.");
- break;
- case 4:
- strcat(info,
- " screams, \"Give me everything and get away!\"");
- break;
- case 5:
- strcat(info,
- " says, \"I need a new robe. I'll buy it with your money.\"");
- break;
- case 6:
- strcat(info,
- " screams, \"I want your rings! And amulets! And... EVERYTHING!\"");
- break;
- case 7:
- strcat(info,
- " screams, \"I hate dirty adventurers like you.\"");
- break;
- case 8:
- strcat(info, " says, \"How can you wear that ugly dress?\"");
- break;
- case 9:
- strcat(info, " screams, \"Die, beast!\"");
- break;
- }
- break; // end Agnes
-
- case MONS_MAUD: // warrior princess looking for sword "Entarex"
- switch (random2(11))
- {
- case 0:
- strcat(info, " screams, \"Submit or die!\"");
- break;
- case 1:
- strcat(info, " screams, \"Give me \"Entarex\"!\"");
- break;
- case 2:
- strcat(info,
- " screams, \"If you give me \"Entarex\", I'll let you live!\"");
- break;
- case 3:
- strcat(info, " frowns.");
- break;
- case 4:
- strcat(info, " looks upset.");
- break;
- case 5:
- strcat(info, " screams, \"You can't face my power!\"");
- break;
- case 6:
- strcat(info,
- " screams, \"Give me that sword! Immediately!\"");
- break;
- case 7:
- strcat(info,
- " screams, \"Your life or \"Entarex\"! You must choose.\"");
- break;
- case 8:
- strcat(info, " screams, \"I want it!\"");
- break;
- case 9:
- strcat(info, " screams, \"Die, you thief!\"");
- break;
- case 10:
- // needed at least one in here to tie to the amnesia
- // scroll reference -- bwr
- strcat(info, " asks \"Will you think of me as you die?\"");
- break;
- }
- break; // end Maud
-
- // wizard looking for bodyparts as spell components
- case MONS_FRANCIS:
- switch (random2(15))
- {
- case 0:
- strcat(info,
- " says, \"You've nice eyes. I could use them.\"");
- break;
- case 1:
- strcat(info, " says, \"Excuse me, but I need your head.\"");
- break;
- case 2:
- strcat(info, " says, \"I only need a few of your organs!\"");
- break;
- case 3:
- strcat(info, " ponders the situation.");
- break;
- case 4:
- strcat(info, " looks for a scalpel.");
- break;
-
- case 5:
- simple_monster_message( monster, " casts a spell",
- MSGCH_MONSTER_SPELL );
-
- strcat(info, "'s hands start to glow with a soft light.");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
-
- case 6:
- strcat(info, " says, \"This won't hurt a bit.\"");
- break;
- case 7:
- strcat(info, " throws something at you.");
- break;
- case 8:
- strcat(info, " says, \"I want you in my laboratory!\"");
- break;
- case 9:
- strcat(info, " says, \"What about a little dissection?\"");
- break;
- case 10:
- strcat(info,
- " says, \"I have something special for you.\"");
- break;
- case 11:
- strcat(info,
- " screams, \"Don't move! I want to cut off your ear!\"");
- break;
- case 12:
- strcat(info,
- " says, \"What about your heart? Do you need it?\"");
- break;
- case 13:
- strcat(info,
- " says, \"Did you know that corpses are an important natural resource?\"");
- break;
- case 14:
- strcat(info,
- " says, \"Don't worry, I'll only take what I need.\"");
- break;
- }
- break; // end Francis
-
- case MONS_RUPERT: // crazy adventurer
- switch (random2(11))
- {
- case 0:
- strcat(info, " says, \"You are a monster, aren't you?\"");
- break;
- case 1:
- strcat(info, " screams, \"Die, monster!\"");
- break;
- case 2:
- strcat(info, " screams, \"Give me the Holy Grail!\"");
- break;
- case 3:
- strcat(info, " screams, \"Red! No, blue!\"");
- break;
- case 4:
- strcat(info, " looks confused.");
- break;
- case 5:
- strcat(info, " looks excited.");
- break;
- case 6:
- strcat(info, " shouts, \"I'm a great and powerful hero!\"");
- break;
- case 7:
- strcat(info,
- " screams, \"Get ready! I'll kill you! Or something like it...\"");
- break;
- case 8:
- strcat(info,
- " says, \"My Mom always said, kill them all.\"");
- break;
- case 9:
- strcat(info,
- " screams, \"You killed all those lovely monsters, you murderer!\"");
- break;
- case 10:
- strcat(info, " screams, \"Hurray!\"");
- break;
- }
- break; // end Rupert
-
- case MONS_NORRIS: // enlighten but crazy man
- switch (random2(24))
- {
- case 0:
- strcat(info, " sings \"Hare Rama, Hare Krishna!\"");
- break;
- case 1:
- strcat(info, " smiles at you.");
- break;
- case 2:
- strcat(info,
- " says, \"After death you'll find inner peace.\"");
- break;
- case 3:
- strcat(info,
- " says, \"Life is just suffering. I'll help you.\"");
- break;
- case 4:
- strcat(info, " is surrounded with aura of peace.");
- break;
- case 5:
- strcat(info, " looks very balanced.");
- break;
- case 6:
- strcat(info, " says, \"Don't resist. I'll do it for you.\"");
- break;
- case 7:
- strcat(info, " screams, \"Enter NIRVANA! Now!\"");
- break;
- case 8:
- strcat(info, " says, \"Death is just a liberation!\"");
- break;
- case 9:
- strcat(info,
- " says, \"Feel free to die. It's great thing.\"");
- break;
- case 10:
- strcat(info, " says, \"OHM MANI PADME HUM!\"");
- break;
-
- case 11:
- strcat(info, " mumbles some mantras.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
-
- case 12:
- strcat(info, " says, \"Breathe deeply.\"");
- break;
- case 13:
- strcat(info, " screams, \"Love! Eternal love!\"");
- break;
- case 14:
- strcat(info,
- " screams, \"Peace! I bring you eternal peace!\"");
- break;
- case 15:
- strcat(info,
- " sighs \"Enlightenment is such responsibility.\"");
- break;
- case 16:
- strcat(info, " looks relaxed.");
- break;
- case 17:
- strcat(info, " screams, \"Free your soul! Die!\"");
- break;
- case 18:
- strcat(info, " screams, \"Blow your mind!\"");
- break;
- case 19:
- strcat(info,
- " says, \"The Orb is only a myth. Forget about it.\"");
- break;
- case 20:
- strcat(info, " says, \"It's all maya.\"");
- break;
- case 21:
- strcat(info, " says, \"Drop out!\"");
- break;
- case 22:
- strcat(info,
- " sings, \"Peace now, freedom now! Peace now, freedom now!\"");
- break;
- case 23:
- strcat(info, " says, \"This is called Combat Meditation.\"");
- break;
- }
- break; // end Norris
-
- case MONS_MARGERY: // powerful sorceress, guarding the ORB
- switch (random2(22))
- {
- case 0:
- strcat(info, " says, \"You are dead.\"");
- break;
- case 1:
- strcat(info, " looks very self-confident.");
- break;
- case 2:
- strcat(info, " screams, \"You must be punished!\"");
- break;
- case 3:
- strcat(info, " screams, \"You can't withstand my power!\"");
- break;
-
- case 4:
- simple_monster_message( monster, " casts a spell.",
- MSGCH_MONSTER_SPELL );
-
- strcat(info, " is surrounded with aura of power.");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
-
- case 5:
- strcat(info, "'s eyes start to glow with a red light.");
- break;
- case 6:
- strcat(info,
- "'s eyes start to glow with a green light.");
- break;
- case 7:
- strcat(info, "'s eyes start to glow with a blue light.");
- break;
- case 8:
- strcat(info, " screams, \"All trespassers must die!\"");
- break;
- case 9:
- strcat(info, " says, \"Die!\"");
- break;
- case 10:
- strcat(info, " screams, \"You'll have to get past me!\"");
- break;
-
- case 11:
- simple_monster_message( monster, " casts a spell.",
- MSGCH_MONSTER_SPELL );
-
- strcat(info, " becomes transparent for a moment.");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
-
- case 12:
- strcat(info, " gestures.");
- msg_type = MSGCH_MONSTER_SPELL;
- break;
-
- case 13:
- simple_monster_message( monster, " casts a spell.",
- MSGCH_MONSTER_SPELL );
-
- strcat(info, "'s hands start to glow.");
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
-
- case 14:
- strcat(info, " screams, \"Ergichanteg reztahaw!\"");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("You feel really bad.", MSGCH_WARN);
- return (true);
-
- case 15:
- strcat(info, " screams, \"You are doomed!\"");
- break;
- case 16:
- strcat(info, " screams, \"Nothing can help you.\"");
- break;
- case 17:
- strcat(info, " screams, \"Death is my middle name!\"");
- break;
-
- case 18:
- strcat(info, " gestures.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("You feel doomed.", MSGCH_WARN);
- return (true);
-
- case 19:
- strcat(info, " gestures.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("You feel weakened.", MSGCH_WARN);
- return (true);
-
- case 20:
- strcat(info, " throws some purple powder towards you.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("You feel cursed.", MSGCH_WARN);
- return (true);
-
- case 21:
- strcat(info,
- " screams, \"The ORB is only a tale, but I will kill you anyway!");
- break;
- }
- break; // end Margery
-
- case MONS_IJYB: // twisted goblin
- switch (random2(14))
- {
- case 0:
- strcat(info, " screams, \"Die!\"");
- break;
- case 1:
- strcat(info, " screams, \"Me kill you!\"");
- break;
- case 2:
- strcat(info, " screams, \"Me stronger than you!\"");
- break;
- case 3:
- case 4:
- strcat(info, " grins evilly.");
- break;
- case 5:
- strcat(info, " screams, \"It's all mine!\"");
- break;
- case 6:
- strcat(info, " screams, \"Get away!\"");
- break;
- case 7:
- strcat(info, " screams, \"Level is mine! All mine!\"");
- break;
- case 8:
- strcat(info, " screams, \"I cut your head off!\"");
- break;
- case 9:
- strcat(info, " screams, \"I dance on your bones!\"");
- break;
- case 10:
- strcat(info, " screams, \"Me very upset!\"");
- break;
- case 11:
- strcat(info, " screams, \"You nasty! Big nasty!\"");
- break;
- case 12:
- strcat(info, " screams, \"No! No, no, no, no!\"");
- break;
- case 13:
- strcat(info, " screams, \"I no like you!\"");
- break;
- }
- break; // end IJYB
-
- case MONS_BLORK_THE_ORC: // unfriendly orc
- switch (random2(21))
- {
- case 0:
- strcat(info, " screams, \"I don't like you!\"");
- break;
- case 1:
- strcat(info, " screams, \"I'm going to kill you!\"");
- break;
- case 2:
- strcat(info,
- " screams, \"I'm much stronger than you!\"");
- break;
- case 3:
- case 4:
- strcat(info, " grins evilly.");
- break;
- case 5:
- strcat(info, " frowns.");
- break;
- case 6:
- case 7:
- case 8:
- case 9:
- strcat(info, " looks angry.");
- break;
- case 10:
- strcat(info,
- " screams, \"I'll eat your brain! And then I'll vomit it back up!\"");
- break;
- case 11:
- strcat(info,
- " screams, \"You are the ugliest creature I've ever seen!\"");
- break;
- case 12:
- strcat(info, " screams, \"I'll cut your head off!\"");
- break;
- case 13:
- strcat(info, " screams, \"I'll break your legs!\"");
- break;
- case 14:
- strcat(info, " screams, \"I'll break your arms!\"");
- break;
- case 15:
- strcat(info,
- " screams, \"I'll crush all your ribs! One by one!\"");
- break;
- case 16:
- strcat(info,
- " screams, \"I'll make a cloak from your skin!\"");
- break;
- case 17:
- strcat(info,
- " screams, \"I'll decorate my home with your organs!\"");
- break;
- case 18:
- strcat(info, " screams, \"Die!\"");
- break;
- case 19:
- strcat(info,
- " screams, \"I'll cover the dungeon with your blood!\"");
- break;
- case 20:
- strcat(info, " screams, \"I'll drink your blood! Soon!\"");
- break;
- }
- break; // end Blork
-
- case MONS_EROLCHA: // ugly ogre
- switch (random2(11))
- {
- case 0:
- strcat(info, " tries to grin evilly.");
- break;
- case 1:
- strcat(info, " screams, \"Eat!\"");
- break;
- case 2:
- strcat(info, " screams, \"Stand! Erolcha hit you!\"");
- break;
- case 3:
- strcat(info, " screams, \"Blood!\"");
- break;
- case 4:
- strcat(info, " screams, \"Erolcha kill you!\"");
- break;
- case 5:
- strcat(info,
- " screams, \"Erolcha crush your head!\"");
- break;
- case 6:
- strcat(info, " roars.");
- break;
- case 7:
- strcat(info, " growls.");
- break;
- case 8:
- strcat(info, " screams, \"Lunch!\"");
- break;
- case 9:
- strcat(info, " screams, \"Erolcha happy to kill you!\"");
- break;
- case 10:
- strcat(info, " screams, \"Erolcha angry!\"");
- break;
- }
- break; // end Erolcha
-
- case MONS_URUG: // orc hired to kill you
- switch (random2(11))
- {
- case 0:
- strcat(info, " grins evilly.");
- break;
- case 1:
- strcat(info, " screams, \"Die!\"");
- break;
- case 2:
- strcat(info, " screams, \"I'm going to kill you! Now!\"");
- break;
- case 3:
- strcat(info, " screams, \"Blood and destruction!\"");
- break;
- case 4:
- strcat(info,
- " sneers, \"Innocent? I'll kill you anyway.\"");
- break;
- case 5:
- strcat(info,
- " screams, \"I'll get 30 silver pieces for your head!\"");
- break;
- case 6:
- strcat(info, " roars.");
- break;
- case 7:
- strcat(info, " howls with blood-lust.");
- break;
- case 8:
- strcat(info, " screams, \"You are already dead.\"");
- break;
- case 9:
- strcat(info, " says, \"Maybe you aren't ");
- strcat(info, you.your_name);
- strcat(info, ". It doesn't matter.\"");
- break;
- case 10:
- strcat(info, " screams, \"I love blood!\"");
- break;
- }
- break; // end Urug
-
- case MONS_SNORG: // troll
- switch (random2(16))
- {
- case 0:
- strcat(info, " grins.");
- break;
- case 1:
- case 2:
- case 3:
- strcat(info, " smells terrible.");
- break;
- case 4:
- case 5:
- case 6:
- strcat(info, " looks very hungry.");
- break;
- case 7:
- strcat(info, " screams, \"Snack!\"");
- break;
- case 8:
- strcat(info, " roars.");
- break;
- case 9:
- strcat(info, " says, \"Food!\"");
- break;
- case 10:
- strcat(info, " screams, \"Snorg hungry!\"");
- break;
- case 11:
- strcat(info, " screams, \"Snorg very, very hungry!\"");
- case 12:
- strcat(info, " says, \"Snorg eat you.\"");
- break;
- case 13:
- strcat(info, " says, \"You food?\"");
- break;
- case 14:
- strcat(info, " says, \"Yum, yum.\"");
- break;
- case 15:
- strcat(info, " burps.");
- break;
- }
- break; // end Snorg
-
- case MONS_XTAHUA: // ancient dragon
- switch (random2(13))
- {
- case 0:
- strcat(info, " roars, \"DIE, PUNY ONE!\"");
- break;
- case 1:
- strcat(info, " growls, \"YOU BORE ME SO.\"");
- break;
- case 2:
- strcat(info, " rumbles, \"YOU'RE BARELY A SNACK.\"");
- break;
- case 3:
- strcat(info, " roars, \"I HATE BEING BOTHERED!\"");
- break;
- case 4:
- strcat(info, " roars, \"I HOPE YOU'RE TASTY!\"");
- break;
- case 5:
- strcat(info, " roars, \"BAH! BLOODY ADVENTURERS.\"");
- break;
- case 6:
- strcat(info, " roars, \"FACE MY WRATH!\"");
- break;
- case 7:
- strcat(info, " glares at you.");
- break;
- case 8:
- strcat(info,
- " roars, \"COMING HERE WAS YOUR LAST MISTAKE!\"");
- break;
- case 9:
- strcat(info,
- " roars, \"I'VE KILLED HUNDREDS OF ADVENTURERS!\"");
- break;
- case 10:
- case 11:
- case 12:
- strcat(info, " roars horribly.");
- mpr(info, MSGCH_TALK);
- mpr("You are afraid.", MSGCH_WARN);
- return (true);
- }
- break; // end Xtahua
-
- case MONS_BORIS: // ancient lich
- switch (random2(24))
- {
- case 0:
- strcat(info, " says, \"I didn't invite you.\"");
- break;
- case 1:
- strcat(info, " says, \"You can't imagine my power.\"");
- break;
- case 2:
- strcat(info,
- " says, \"Orb? You want the Orb? You'll never get it.\"");
- break;
-
- case 3:
- strcat(info, " says, \"The world, the flesh, and the devil.\"");
- break;
-
- case 4:
- strcat(info, " gestures.");
- break;
-
- case 5:
- strcat(info, " stares at you.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("You feel drained.", MSGCH_WARN);
- return (true);
-
- case 6:
- strcat(info, " stares at you.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("You feel weakened.", MSGCH_WARN);
- return (true);
-
- case 7:
- strcat(info, " stares at you.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("You feel troubled.", MSGCH_WARN);
- return (true);
-
- case 8:
- strcat(info, " says \"Magic. You know nothing about it.\"");
- break;
-
- case 9:
- strcat(info, " says, \"My power is unlimited.\"");
- break;
- case 10:
- strcat(info, " says, \"You can't kill me. I'm immortal.\"");
- break;
-
- case 11:
- strcat(info, " casts a spell.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("Your equipment suddenly seems to weigh more.", MSGCH_WARN);
- return (true);
-
- case 12:
- strcat(info,
- " says, \"I know the secret of eternal life. Do you?\"");
- break;
- case 13:
- strcat(info, " says, \"I'll be back.\"");
- break;
-
- case 14:
- strcat(info, " casts a spell.");
- mpr(info, MSGCH_MONSTER_SPELL);
- canned_msg( MSG_YOU_RESIST );
- return (true);
-
- case 15:
- strcat(info, " casts a spell.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("Suddenly you are surrounded with a pale green light.", MSGCH_WARN);
- return (true);
-
- case 16:
- strcat(info, " casts a spell.");
- mpr(info, MSGCH_MONSTER_SPELL);
- mpr("You have a terrible headache.", MSGCH_WARN);
- return (true);
-
- case 17:
- strcat(info,
- " says, \"I know your future. Your future is death.\"");
- break;
- case 18:
- strcat(info, " says, \"Who wants to live forever? Me.\"");
- break;
- case 19:
- strcat(info, " laughs.");
- break;
- case 20:
- strcat(info, " says, \"Join the legion of my servants.\"");
- break;
- case 21:
- strcat(info, " says, \"There's only one solution for you. To die.\"");
- break;
- case 22:
- strcat(info, " says, \"You can never win.\"");
- break;
- case 23:
- simple_monster_message( monster, " casts a spell.",
- MSGCH_MONSTER_SPELL );
-
- strcat( info, " speeds up." );
- msg_type = MSGCH_MONSTER_ENCHANT;
- break;
- }
- break; // end BORIS
-
-
- case MONS_DEATH_COB:
- if (one_chance_in(2000))
- {
- mpr("The death cob makes a corny joke.", MSGCH_TALK);
- return (true);
- }
- return (false);
-
- case MONS_KILLER_KLOWN: // Killer Klown - guess!
- switch (random2(10))
- {
- case 0:
- strcat(info, " giggles crazily.");
- break;
- case 1:
- strcat(info, " laughs merrily.");
- break;
- case 2:
- strcat(info, " beckons to you.");
- break;
- case 3:
- strcat(info, " does a flip.");
- break;
- case 4:
- strcat(info, " does a somersault.");
- break;
- case 5:
- strcat(info, " smiles at you.");
- break;
- case 6:
- strcat(info, " grins with merry abandon.");
- break;
- case 7:
- strcat(info, " howls with blood-lust!");
- break;
- case 8:
- strcat(info, " pokes out its tongue.");
- break;
- case 9:
- strcat(info, " says, \"Come and play with me!\"");
- break;
- }
- break; // end Killer Klown
+ if (match)
+ line = line.substr(pos + 1);
+ }
- default:
- strcat(info,
- " says, \"I don't know what to say. It's a bug.\"");
- break;
- } // end monster->type - monster type switch
- } // end default
+ mpr(line.c_str(), msg_type);
+ }
- mpr(info, msg_type);
return true;
} // end mons_speaks = end of routine
diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc
index 574843070b..d2e574b950 100644
--- a/crawl-ref/source/monstuff.cc
+++ b/crawl-ref/source/monstuff.cc
@@ -2134,11 +2134,34 @@ static void handle_nearby_ability(monsters *monster)
return;
}
+#define MON_SPEAK_CHANCE 21
+
if (mons_class_flag(monster->type, M_SPEAKS)
- && monster->behaviour != BEH_WANDER && one_chance_in(21))
+ && monster->behaviour != BEH_WANDER && one_chance_in(MON_SPEAK_CHANCE))
{
mons_speaks(monster);
}
+ else if (get_mon_shape(monster) >= MON_SHAPE_QUADRUPED)
+ {
+ // Non-humanoid-ish monsters have a low chance of speaking
+ // wihtout the M_SPEAKS flag, to give the dungeon some
+ // atmosphere/flavor.
+ int chance = MON_SPEAK_CHANCE * 4;
+
+ // Band members are a lot less likely to speak, since there's
+ // a lot of them.
+ if (testbits(monster->flags, MF_BAND_MEMBER))
+ chance *= 10;
+
+ // However, confused and fleeing monsters are more interesting.
+ if (monster->behaviour == BEH_FLEE)
+ chance /= 2;
+ if (monster->has_ench(ENCH_CONFUSION))
+ chance /= 2;
+
+ if (one_chance_in(chance))
+ mons_speaks(monster);
+ }
switch (monster->type)
{
diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc
index dd080ef687..53cd62555d 100644
--- a/crawl-ref/source/view.cc
+++ b/crawl-ref/source/view.cc
@@ -40,12 +40,12 @@
#include "cio.h"
#include "cloud.h"
#include "clua.h"
+#include "database.h"
#include "debug.h"
#include "delay.h"
#include "direct.h"
#include "dungeon.h"
#include "initfile.h"
-#include "insult.h"
#include "itemprop.h"
#include "luadgn.h"
#include "macro.h"
@@ -744,95 +744,202 @@ inline static void beogh_follower_convert(monsters *monster)
}
}
-static void handle_monster_shouts(monsters* monster)
+static void handle_monster_shouts(monsters* monster, bool force = false)
{
- if (you.turn_is_over
- && mons_shouts(monster->type) != S_SILENT
- && random2(30) >= you.skills[SK_STEALTH])
+ if (!force
+ && (!you.turn_is_over || random2(30) < you.skills[SK_STEALTH]))
+ return;
+
+ // Get it once, since monster might be S_RANDOM, in which case
+ // mons_shouts() will return a different value every time.
+ shout_type type = mons_shouts(monster->type);
+
+ // Silent monsters can give noiseless "visual shouts" if the
+ // player can see them, in which case silence isn't checked for.
+ if (mons_friendly(monster)
+ || (type == S_SILENT && !player_monster_visible(monster))
+ || (type != S_SILENT && (silenced(you.x_pos, you.y_pos)
+ || silenced(monster->x, monster->y))))
+ return;
+
+ int noise_level = 8;
+ std::string default_msg_key;
+
+ switch (type)
{
- int noise_level = 8;
+ case NUM_SHOUTS:
+ case S_RANDOM:
+ default_msg_key = "__BUGGY";
+ break;
+ case S_SILENT:
+ default_msg_key = "";
+ noise_level = 0;
+ break;
+ case S_SHOUT:
+ default_msg_key = "__SHOUT";
+ break;
+ case S_BARK:
+ default_msg_key = "__BARK";
+ break;
+ case S_SHOUT2:
+ default_msg_key = "__TWO_SHOUTS";
+ noise_level = 12;
+ break;
+ case S_ROAR:
+ default_msg_key = "__ROAR";
+ noise_level = 12;
+ break;
+ case S_SCREAM:
+ default_msg_key = "__SCREAM";
+ break;
+ case S_BELLOW:
+ default_msg_key = "__BELLOW";
+ break;
+ case S_SCREECH:
+ default_msg_key = "__SCREECH";
+ break;
+ case S_BUZZ:
+ default_msg_key = "__BUZZ";
+ break;
+ case S_MOAN:
+ default_msg_key = "__MOAN";
+ break;
+ case S_WHINE:
+ default_msg_key = "__WHINE";
+ break;
+ case S_CROAK:
+ default_msg_key = "__CROAK";
+ break;
+ case S_GROWL:
+ default_msg_key = "__GROWL";
+ break;
+ case S_HISS:
+ default_msg_key = "__HISS";
+ noise_level = 4; // not very loud -- bwr
+ break;
- if (!mons_friendly(monster)
- && (!silenced(you.x_pos, you.y_pos)
- && !silenced(monster->x, monster->y)))
- {
- if (mons_is_demon( monster->type ) && coinflip())
- {
- if (monster->type == MONS_IMP
- || monster->type == MONS_WHITE_IMP
- || monster->type == MONS_SHADOW_IMP)
- {
- imp_taunt( monster );
- }
- else
- {
- demon_taunt( monster );
- }
- }
- else
- {
- std::string msg = "You hear ";
- switch (mons_shouts(monster->type))
- {
- case S_SILENT:
- case NUM_SHOUTS:
- case S_RANDOM:
- msg += "buggy behaviour!";
- break;
- case S_SHOUT:
- msg += "a shout!";
- break;
- case S_BARK:
- msg += "a bark!";
- break;
- case S_SHOUT2:
- msg += "two shouts!";
- noise_level = 12;
- break;
- case S_ROAR:
- msg += "a roar!";
- noise_level = 12;
- break;
- case S_SCREAM:
- msg += "a hideous shriek!";
- break;
- case S_BELLOW:
- msg += "a bellow!";
- break;
- case S_SCREECH:
- msg += "a screech!";
- break;
- case S_BUZZ:
- msg += "an angry buzzing noise.";
- break;
- case S_MOAN:
- msg += "a chilling moan.";
- break;
- case S_WHINE:
- msg += "an irritating high-pitched whine.";
- break;
- case S_CROAK:
- if (coinflip())
- msg += "a loud, deep croak!";
- else
- msg += "a croak.";
- break;
- case S_GROWL:
- msg += "an angry growl!";
- break;
- case S_HISS:
- msg += "an angry hiss!";
- noise_level = 4; // not very loud -- bwr
- break;
- }
- msg::streams(MSGCH_SOUND) << msg << std::endl;
- }
- }
+ // Loudness setting for shouts that are only defined in dat/shout.txt
+ case S_VERY_SOFT:
+ default_msg_key = "";
+ noise_level = 4;
+ break;
+ case S_SOFT:
+ default_msg_key = "";
+ noise_level = 6;
+ break;
+ case S_NORMAL:
+ default_msg_key = "";
+ noise_level = 8;
+ break;
+ case S_LOUD:
+ default_msg_key = "";
+ noise_level = 10;
+ break;
+ case S_VERY_LOUD:
+ default_msg_key = "";
+ noise_level = 12;
+ break;
+ }
- noisy( noise_level, monster->x, monster->y );
+ // Use get_monster_data(monster->type) to bypass mon_shouts()
+ // replacing S_RANDOM with a random value.
+ if (mons_is_demon( monster->type ) && coinflip()
+ && (type != S_SILENT ||
+ get_monster_data(monster->type)->shouts == S_RANDOM))
+ {
+ noise_level = 8;
+ default_msg_key = "__DEMON_TAUNT";
}
+
+ std::string msg, suffix;
+ std::string key = mons_type_name(monster->type, DESC_PLAIN);
+
+ // Pandemonium demons have random names, so use "pandemonium lord"
+ if (monster->type == MONS_PANDEMONIUM_DEMON)
+ key = "pandemonium lord";
+ // Search for player ghost shout by the ghost's class.
+ else if (monster->type == MONS_PLAYER_GHOST)
+ {
+ const ghost_demon &ghost = *(monster->ghost);
+ std::string ghost_class = get_class_name(ghost.values[GVAL_CLASS]);
+
+ key = ghost_class + " player ghost";
+
+ default_msg_key = "player ghost";
+ }
+
+ // Tries to find an entry for "name seen" or "name unseen",
+ // and if no such entry exists then looks simply for "name".
+ if (player_monster_visible(monster))
+ suffix = " seen";
+ else
+ suffix = " unseen";
+
+ msg = getShoutString(key, suffix);
+
+ if (msg == "__DEFAULT" || msg == "__NEXT")
+ msg = getShoutString(default_msg_key, suffix);
+ else if (msg == "")
+ {
+ // See if there's a shout for all monsters using the
+ // same glyph/symbol
+ std::string glyph_key = "'";
+
+ // Database keys are case-insensitve.
+ if (isupper(mons_char(monster->type)))
+ glyph_key += "cap-";
+
+ glyph_key += mons_char(monster->type);
+ glyph_key += "'";
+ msg = getShoutString(glyph_key, suffix);
+
+ if (msg == "" || msg == "__DEFAULT")
+ msg = getShoutString(default_msg_key, suffix);
+ }
+
+ if (default_msg_key == "__BUGGY")
+ msg::streams(MSGCH_SOUND) << "You hear something buggy!"
+ << std::endl;
+ else if ((msg == "" || msg == "__NONE")
+ && mons_shouts(monster->type) == S_SILENT)
+ ; // No "visual shout" defined for silent monster, do nothing
+ else if (msg == "")
+ {
+ msg::streams(MSGCH_DIAGNOSTICS)
+ << "No shout entry for default shout type '"
+ << default_msg_key << "'" << std::endl;
+ msg::streams(MSGCH_SOUND) << "You hear something buggy!"
+ << std::endl;
+ }
+ else if (msg == "__NONE")
+ {
+ msg::streams(MSGCH_DIAGNOSTICS)
+ << "__NONE returned as shout for non-silent monster '"
+ << default_msg_key << "'" << std::endl;
+ msg::streams(MSGCH_SOUND) << "You hear something buggy!"
+ << std::endl;
+ }
+ else
+ {
+ msg = do_mon_str_replacements(msg, monster);
+
+ if (mons_shouts(monster->type) == S_SILENT)
+ msg::streams(MSGCH_TALK) << msg << std::endl;
+ else
+ msg::streams(MSGCH_SOUND) << msg << std::endl;
+ }
+
+ if (noise_level > 0)
+ noisy( noise_level, monster->x, monster->y );
}
+#ifdef WIZARD
+void force_monster_shout(monsters* monster)
+{
+ handle_monster_shouts(monster, true);
+}
+#endif
+
inline static bool update_monster_grid(const monsters *monster)
{
const int ex = monster->x - you.x_pos + 9;