From ebe3faa645862641354b8bddf6c52dccc108a16c Mon Sep 17 00:00:00 2001 From: j-p-e-g Date: Tue, 4 Sep 2007 11:32:42 +0000 Subject: 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 --- crawl-ref/source/database.cc | 357 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 297 insertions(+), 60 deletions(-) (limited to 'crawl-ref/source/database.cc') 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 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 description_txt_paths() -{ - std::vector txt_file_names; - std::vector 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 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 parts; + std::vector weights; + + std::vector 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 description_txt_paths() +{ + std::vector txt_file_names; + std::vector 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 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()); +} -- cgit v1.2.3-54-g00ecf