summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source/database.cc
diff options
context:
space:
mode:
authorj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2007-09-04 11:32:42 +0000
committerj-p-e-g <j-p-e-g@c06c8d41-db1a-0410-9941-cceddc491573>2007-09-04 11:32:42 +0000
commitebe3faa645862641354b8bddf6c52dccc108a16c (patch)
tree5a349309cbde8c3803b8ca83b5833e63392e7c42 /crawl-ref/source/database.cc
parent746b402561b6b27625c5c1ac33028da9aa846b18 (diff)
downloadcrawl-ref-ebe3faa645862641354b8bddf6c52dccc108a16c.tar.gz
crawl-ref-ebe3faa645862641354b8bddf6c52dccc108a16c.zip
Implementing patch 1775415 (outsourcing monster speech) by
zelgadis. Currently, shout.txt and speak.txt share in with the .des files in /dat. That should be changed, but I've no idea how to do this. Also implementing a bug fix by ennewalker (1787428). git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@2052 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source/database.cc')
-rw-r--r--crawl-ref/source/database.cc357
1 files changed, 297 insertions, 60 deletions
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());
+}