diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8c4fa1ea6..f4d6531f1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,3 +37,4 @@ add_subdirectory(Main)
add_subdirectory(igor)
add_subdirectory(mihail)
add_subdirectory(xbrzscale)
+add_subdirectory(fantasyname)
diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt
index b33e7dc3d..6d37f3c7a 100644
--- a/Main/CMakeLists.txt
+++ b/Main/CMakeLists.txt
@@ -15,8 +15,8 @@ set_source_files_properties(
PROPERTIES HEADER_FILE_ONLY TRUE)
add_executable(ivan ${IVAN_SOURCES} Resource/Ivan.rc)
-target_include_directories(ivan PUBLIC Include ../Felib/Include ../audio)
-target_link_libraries(ivan FeLib FeAudio SDL2_mixer pcre xbrzscale)
+target_include_directories(ivan PUBLIC Include ../Felib/Include ../audio ../fantasyname)
+target_link_libraries(ivan FeLib FeAudio SDL2_mixer pcre xbrzscale fantasyname)
if(UNIX)
install(TARGETS ivan DESTINATION "${CMAKE_INSTALL_BINDIR}")
diff --git a/Main/Include/game.h b/Main/Include/game.h
index 995fc0369..9dc6a7d56 100644
--- a/Main/Include/game.h
+++ b/Main/Include/game.h
@@ -235,7 +235,7 @@ class game
static void CalculateGodNumber();
static void IncreaseTick() { ++Tick; }
static ulong GetTick() { return Tick; }
- static festring GetAutoSaveFileName() { return game::GetSaveDir() + PlayerName + ".AutoSave"; }
+ static festring GetAutoSaveFileName() { return SaveName() + ".AutoSave"; }
static int DirectionQuestion(cfestring&, truth = true, truth = false);
static void RemoveSaves(truth = true,truth onlyBackups=false);
static truth IsInWilderness() { return InWilderness; }
diff --git a/Main/Include/iconf.h b/Main/Include/iconf.h
index 0b852af15..fb2773f6f 100644
--- a/Main/Include/iconf.h
+++ b/Main/Include/iconf.h
@@ -20,6 +20,7 @@ class ivanconfig
{
public:
static cfestring& GetDefaultName() { return DefaultName.Value; }
+ static cfestring& GetFantasyNamePattern() { return FantasyNamePattern.Value; }
static cfestring& GetDefaultPetName() { return DefaultPetName.Value; }
static long GetAutoSaveInterval() { return AutoSaveInterval.Value; }
static long GetContrast() { return Contrast.Value; }
@@ -94,6 +95,7 @@ class ivanconfig
static void ContrastDisplayer(const numberoption*, festring&);
static void DirectionKeyMapDisplayer(const cycleoption*, festring&);
static truth DefaultNameChangeInterface(stringoption*);
+ static truth FantasyNameChangeInterface(stringoption* O);
static truth DefaultPetNameChangeInterface(stringoption*);
static truth AutoSaveIntervalChangeInterface(numberoption*);
static truth XBRZSquaresAroundPlayerChangeInterface(numberoption* O);
@@ -143,6 +145,7 @@ class ivanconfig
static void BackGroundDrawer();
static stringoption DefaultName;
+ static stringoption FantasyNamePattern;
static stringoption DefaultPetName;
static numberoption AutoSaveInterval;
static truthoption AltAdentureInfo;
diff --git a/Main/Source/game.cpp b/Main/Source/game.cpp
index 09005e856..92dfae08d 100644
--- a/Main/Source/game.cpp
+++ b/Main/Source/game.cpp
@@ -55,6 +55,7 @@
#include "team.h"
#include "whandler.h"
#include "wsquare.h"
+#include "namegen.h"
#define DBGMSG_BLITDATA
#include "dbgmsgproj.h"
@@ -619,14 +620,23 @@ void game::PrepareStretchRegionsLazy(){ // the ADD order IS important IF they ov
UpdateSRegionsXBRZ();
}
+void FantasyName(festring& rfsName){ DBG2(rfsName.CStr(),ivanconfig::GetFantasyNamePattern().CStr());
+ if(ivanconfig::GetFantasyNamePattern().IsEmpty())return;
+
+ NameGen::Generator gen(ivanconfig::GetFantasyNamePattern().CStr());
+ rfsName << gen.toString().c_str(); DBG1(rfsName.CStr());
+}
+
truth game::Init(cfestring& Name)
-{
+{ DBG2(Name.CStr(),ivanconfig::GetDefaultName().CStr());
if(Name.IsEmpty())
{
if(ivanconfig::GetDefaultName().IsEmpty())
{
PlayerName.Empty();
+ FantasyName(PlayerName); DBG1(PlayerName.CStr());
+
if(iosystem::StringQuestion(PlayerName, CONST_S("What is your name? (1-20 letters)"),
v2(30, 46), WHITE, 1, 20, true, true) == ABORTED
|| PlayerName.IsEmpty())
diff --git a/Main/Source/iconf.cpp b/Main/Source/iconf.cpp
index d8a3ceacf..8f2a16630 100644
--- a/Main/Source/iconf.cpp
+++ b/Main/Source/iconf.cpp
@@ -27,6 +27,11 @@ stringoption ivanconfig::DefaultName( "DefaultName",
"",
&configsystem::NormalStringDisplayer,
&DefaultNameChangeInterface);
+stringoption ivanconfig::FantasyNamePattern("FantasyNamePattern",
+ "fantasy name generator pattern",
+ "!ss !sV",
+ &configsystem::NormalStringDisplayer,
+ &FantasyNameChangeInterface);
stringoption ivanconfig::DefaultPetName( "DefaultPetName",
"starting pet's default name",
CONST_S("Kenny"),
@@ -433,6 +438,19 @@ truth ivanconfig::DungeonGfxScaleChangeInterface(cycleoption* O)
return true;
}
+truth ivanconfig::FantasyNameChangeInterface(stringoption* O)
+{
+ festring String;
+
+ if(iosystem::StringQuestion(String, CONST_S("Set name generator pattern (recommended \"!ss !sV\"):"),
+ GetQuestionPos(), WHITE, 0, 20, !game::IsRunning(), true) == NORMAL_EXIT)
+ O->ChangeValue(String);
+
+ clearToBackgroundAfterChangeInterface();
+
+ return false;
+}
+
truth ivanconfig::DefaultNameChangeInterface(stringoption* O)
{
festring String;
@@ -762,6 +780,7 @@ void ivanconfig::Initialize()
fsCategory="Core Game Setup";
configsystem::AddOption(fsCategory,&DefaultName);
+ configsystem::AddOption(fsCategory,&FantasyNamePattern);
configsystem::AddOption(fsCategory,&DefaultPetName);
configsystem::AddOption(fsCategory,&AutoSaveInterval);
configsystem::AddOption(fsCategory,&AltAdentureInfo);
diff --git a/fantasyname/CMakeLists.txt b/fantasyname/CMakeLists.txt
new file mode 100644
index 000000000..dea020012
--- /dev/null
+++ b/fantasyname/CMakeLists.txt
@@ -0,0 +1,5 @@
+file(GLOB FANTASYNAME_SOURCES namegen.cc namegen.h)
+
+set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
+
+add_library(fantasyname ${FANTASYNAME_SOURCES})
diff --git a/fantasyname/Makefile b/fantasyname/Makefile
new file mode 100644
index 000000000..0bba830a7
--- /dev/null
+++ b/fantasyname/Makefile
@@ -0,0 +1,18 @@
+.POSIX:
+.SUFFIXES: .cc
+CXX = c++
+CXXFLAGS = -std=c++11 -Wall -Wextra -O3 -g3
+
+all: namegen
+
+namegen: namegen.o example.o
+ $(CXX) $(LDFLAGS) -o $@ namegen.o example.o $(LDLIBS)
+
+namegen.o: namegen.cc
+example.o: example.cc
+
+clean:
+ rm -rf namegen namegen.o example.o
+
+.cc.o:
+ $(CXX) -c $(CXXFLAGS) -o $@ $<
diff --git a/fantasyname/README.md b/fantasyname/README.md
new file mode 100644
index 000000000..9c451ec18
--- /dev/null
+++ b/fantasyname/README.md
@@ -0,0 +1,47 @@
+# Fantasy Name Generator
+
+Four implementations -- JavaScript, C++, Elisp, and Perl -- of the
+[name generator described at RinkWorks](http://rinkworks.com/namegen/).
+The JavaScript and C++ implementations are by far the most mature.
+
+
+## JavaScript
+
+The JavaScript version uses an optimizing template-compiler to create
+an efficient generator object.
+
+```javascript
+var generator = NameGen.compile("sV'i");
+generator.toString(); // => "entheu'loaf"
+generator.toString(); // => "honi'munch"
+```
+
+## C++
+
+The C++ version also uses a template-compiler (based on the one for JavaScript)
+to create an efficient generator object. It requires C++11.
+
+```c++
+NameGen::Generator generator("sV'i");
+generator.toString(); // => "drai'sneeze"
+generator.toString(); // => "ardou'bumble"
+```
+
+## Emacs Lisp
+
+The Emacs Lisp version doesn't include a parser. It operates on
+s-expressions.
+
+```el
+(fset 'generator (apply-partially #'namegen '(s V "'" i)))
+(generator) ; => "essei'knocker"
+(generator) ; => "tiaoe'nit"
+```
+
+## Perl
+
+The Perl version is exceptionally slow, due to a slow parser.
+
+```perl
+generate("sV'i"); # => "echoi'bum"
+```
diff --git a/fantasyname/UNLICENSE b/fantasyname/UNLICENSE
new file mode 100644
index 000000000..68a49daad
--- /dev/null
+++ b/fantasyname/UNLICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
diff --git a/fantasyname/example.cc b/fantasyname/example.cc
new file mode 100644
index 000000000..00afa81a8
--- /dev/null
+++ b/fantasyname/example.cc
@@ -0,0 +1,33 @@
+#include "namegen.h"
+
+#include
+#include
+#include
+
+
+int main(int argc, char **argv)
+{
+ int num = 1;
+ std::setlocale(LC_CTYPE, "");
+ if (argc < 2 || argc > 3 || (argc == 3 && (num = atoi(argv[2])) && num == -1)) {
+ std::cerr << "Usage: " << argv[0] << " [num]\n";
+ std::cerr << " pattern - Template for names to generate.\n";
+ std::cerr << " num - Number of names to generate.\n";
+ return 64;
+ }
+
+ char * pattern = argv[1];
+ // std::cerr << "> pattern = " << pattern << "\n";
+
+ NameGen::Generator generator(pattern);
+
+ std::cerr << "> combinations = " << generator.combinations() << "\n";
+ // std::cerr << "> min = " << generator.min() << "\n";
+ // std::cerr << "> max = " << generator.max() << "\n";
+
+ for (int i = 0; i < num; i++) {
+ std::cout << generator.toString() << "\n";
+ }
+
+ return 0;
+}
diff --git a/fantasyname/namegen.cc b/fantasyname/namegen.cc
new file mode 100644
index 000000000..c8bf964f2
--- /dev/null
+++ b/fantasyname/namegen.cc
@@ -0,0 +1,536 @@
+/**
+ *
+ * @file A fantasy name generator library.
+ * @version 1.0.1
+ * @license Public Domain
+ * @author German M. Bravo (Kronuz)
+ *
+ */
+
+#include "namegen.h"
+
+#include // for move, reverse
+#include // for size_t, mbsrtowcs, wcsrtombs
+#include // for towupper
+#include // for make_unique
+#include // for mt19937, random_device, uniform_real_distribution
+#include // for invalid_argument, out_of_range
+
+
+using namespace NameGen;
+
+
+static std::random_device rd; // Random device engine, usually based on /dev/random on UNIX-like systems
+static std::mt19937 rng(rd()); // Initialize Mersennes' twister using rd to generate the seed
+
+
+// https://isocpp.org/wiki/faq/ctors#static-init-order
+// Avoid the "static initialization order fiasco"
+const std::unordered_map>& Generator::SymbolMap()
+{
+ static auto* const symbols = new std::unordered_map>({
+ {
+ "s", {
+ "ach", "ack", "ad", "age", "ald", "ale", "an", "ang", "ar", "ard",
+ "as", "ash", "at", "ath", "augh", "aw", "ban", "bel", "bur", "cer",
+ "cha", "che", "dan", "dar", "del", "den", "dra", "dyn", "ech", "eld",
+ "elm", "em", "en", "end", "eng", "enth", "er", "ess", "est", "et",
+ "gar", "gha", "hat", "hin", "hon", "ia", "ight", "ild", "im", "ina",
+ "ine", "ing", "ir", "is", "iss", "it", "kal", "kel", "kim", "kin",
+ "ler", "lor", "lye", "mor", "mos", "nal", "ny", "nys", "old", "om",
+ "on", "or", "orm", "os", "ough", "per", "pol", "qua", "que", "rad",
+ "rak", "ran", "ray", "ril", "ris", "rod", "roth", "ryn", "sam",
+ "say", "ser", "shy", "skel", "sul", "tai", "tan", "tas", "ther",
+ "tia", "tin", "ton", "tor", "tur", "um", "und", "unt", "urn", "usk",
+ "ust", "ver", "ves", "vor", "war", "wor", "yer"
+ }
+ },
+ {
+ "v", {
+ "a", "e", "i", "o", "u", "y"
+ }
+ },
+ {
+ "V", {
+ "a", "e", "i", "o", "u", "y", "ae", "ai", "au", "ay", "ea", "ee",
+ "ei", "eu", "ey", "ia", "ie", "oe", "oi", "oo", "ou", "ui"
+ }
+ },
+ {
+ "c", {
+ "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r",
+ "s", "t", "v", "w", "x", "y", "z"
+ }
+ },
+ {
+ "B", {
+ "b", "bl", "br", "c", "ch", "chr", "cl", "cr", "d", "dr", "f", "g",
+ "h", "j", "k", "l", "ll", "m", "n", "p", "ph", "qu", "r", "rh", "s",
+ "sch", "sh", "sl", "sm", "sn", "st", "str", "sw", "t", "th", "thr",
+ "tr", "v", "w", "wh", "y", "z", "zh"
+ }
+ },
+ {
+ "C", {
+ "b", "c", "ch", "ck", "d", "f", "g", "gh", "h", "k", "l", "ld", "ll",
+ "lt", "m", "n", "nd", "nn", "nt", "p", "ph", "q", "r", "rd", "rr",
+ "rt", "s", "sh", "ss", "st", "t", "th", "v", "w", "y", "z"
+ }
+ },
+ {
+ "i", {
+ "air", "ankle", "ball", "beef", "bone", "bum", "bumble", "bump",
+ "cheese", "clod", "clot", "clown", "corn", "dip", "dolt", "doof",
+ "dork", "dumb", "face", "finger", "foot", "fumble", "goof",
+ "grumble", "head", "knock", "knocker", "knuckle", "loaf", "lump",
+ "lunk", "meat", "muck", "munch", "nit", "numb", "pin", "puff",
+ "skull", "snark", "sneeze", "thimble", "twerp", "twit", "wad",
+ "wimp", "wipe"
+ }
+ },
+ {
+ "m", {
+ "baby", "booble", "bunker", "cuddle", "cuddly", "cutie", "doodle",
+ "foofie", "gooble", "honey", "kissie", "lover", "lovey", "moofie",
+ "mooglie", "moopie", "moopsie", "nookum", "poochie", "poof",
+ "poofie", "pookie", "schmoopie", "schnoogle", "schnookie",
+ "schnookum", "smooch", "smoochie", "smoosh", "snoogle", "snoogy",
+ "snookie", "snookum", "snuggy", "sweetie", "woogle", "woogy",
+ "wookie", "wookum", "wuddle", "wuddly", "wuggy", "wunny"
+ }
+ },
+ {
+ "M", {
+ "boo", "bunch", "bunny", "cake", "cakes", "cute", "darling",
+ "dumpling", "dumplings", "face", "foof", "goo", "head", "kin",
+ "kins", "lips", "love", "mush", "pie", "poo", "pooh", "pook", "pums"
+ }
+ },
+ {
+ "D", {
+ "b", "bl", "br", "cl", "d", "f", "fl", "fr", "g", "gh", "gl", "gr",
+ "h", "j", "k", "kl", "m", "n", "p", "th", "w"
+ }
+ },
+ {
+ "d", {
+ "elch", "idiot", "ob", "og", "ok", "olph", "olt", "omph", "ong",
+ "onk", "oo", "oob", "oof", "oog", "ook", "ooz", "org", "ork", "orm",
+ "oron", "ub", "uck", "ug", "ulf", "ult", "um", "umb", "ump", "umph",
+ "un", "unb", "ung", "unk", "unph", "unt", "uzz"
+ }
+ }
+ });
+
+ return *symbols;
+}
+
+
+#ifdef HAVE_CXX14
+using std::make_unique;
+#else
+// make_unique is not available in c++11, so we use this template function
+// to maintain full c++11 compatibility; std::make_unique is part of C++14.
+template
+std::unique_ptr make_unique(Args&&... args)
+{
+ return std::unique_ptr(new T(std::forward(args)...));
+}
+#endif
+
+
+Generator::Generator()
+{
+}
+
+
+Generator::Generator(std::vector>&& generators_) :
+ generators(std::move(generators_))
+{
+}
+
+
+size_t Generator::combinations()
+{
+ size_t total = 1;
+ for (auto& g : generators) {
+ total *= g->combinations();
+ }
+ return total;
+}
+
+
+size_t Generator::min()
+{
+ size_t final = 0;
+ for (auto& g : generators) {
+ final += g->min();
+ }
+ return final;
+}
+
+
+size_t Generator::max()
+{
+ size_t final = 0;
+ for (auto& g : generators) {
+ final += g->max();
+ }
+ return final;
+}
+
+
+std::string Generator::toString() {
+ std::string str;
+ for (auto& g : generators) {
+ str.append(g->toString());
+ }
+ return str;
+}
+
+
+void Generator::add(std::unique_ptr&& g)
+{
+ generators.push_back(std::move(g));
+}
+
+
+Random::Random()
+{
+}
+
+Random::Random(std::vector>&& generators_) :
+ Generator(std::move(generators_))
+{
+}
+
+size_t Random::combinations()
+{
+ size_t total = 0;
+ for (auto& g : generators) {
+ total += g->combinations();
+ }
+ return total ? total : 1;
+}
+
+size_t Random::min()
+{
+ size_t final = -1;
+ for (auto& g : generators) {
+ size_t current = g->min();
+ if (current < final) {
+ final = current;
+ }
+ }
+ return final;
+}
+
+size_t Random::max()
+{
+ size_t final = 0;
+ for (auto& g : generators) {
+ size_t current = g->max();
+ if (current > final) {
+ final = current;
+ }
+ }
+ return final;
+}
+
+
+std::string Random::toString()
+{
+ if (!generators.size()) {
+ return "";
+ }
+ std::uniform_real_distribution distribution(0, generators.size() - 1);
+ int rnd = distribution(rng) + 0.5;
+ return generators[rnd]->toString();
+}
+
+
+Sequence::Sequence()
+{
+}
+
+Sequence::Sequence(std::vector>&& generators_) :
+ Generator(std::move(generators_))
+{
+}
+
+Literal::Literal(const std::string &value_) :
+ value(value_)
+{
+}
+
+size_t Literal::combinations()
+{
+ return 1;
+}
+
+size_t Literal::min()
+{
+ return value.size();
+}
+
+size_t Literal::max()
+{
+ return value.size();
+}
+
+std::string Literal::toString()
+{
+ return value;
+}
+
+Reverser::Reverser(std::unique_ptr&& g)
+{
+ add(std::move(g));
+}
+
+
+std::string Reverser::toString()
+{
+ std::wstring str = towstring(Generator::toString());
+ std::reverse(str.begin(), str.end());
+ return tostring(str);
+}
+
+Capitalizer::Capitalizer(std::unique_ptr&& g)
+{
+ add(std::move(g));
+}
+
+std::string Capitalizer::toString()
+{
+ std::wstring str = towstring(Generator::toString());
+ str[0] = std::towupper(str[0]);
+ return tostring(str);
+}
+
+
+Collapser::Collapser(std::unique_ptr&& g)
+{
+ add(std::move(g));
+}
+
+std::string Collapser::toString()
+{
+ std::wstring str = towstring(Generator::toString());
+ std::wstring out;
+ int cnt = 0;
+ wchar_t pch = L'\0';
+ for (auto ch : str) {
+ if (ch == pch) {
+ cnt++;
+ } else {
+ cnt = 0;
+ }
+ int mch = 2;
+ switch(ch) {
+ case 'a':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'q':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ mch = 1;
+ }
+ if (cnt < mch) {
+ out.push_back(ch);
+ }
+ pch = ch;
+ }
+ return tostring(out);
+}
+
+
+Generator::Generator(const std::string &pattern, bool collapse_triples) {
+ std::unique_ptr last;
+
+ std::stack> stack;
+ std::unique_ptr top = make_unique();
+
+ for (auto c : pattern) {
+ switch (c) {
+ case '<':
+ stack.push(std::move(top));
+ top = make_unique();
+ break;
+ case '(':
+ stack.push(std::move(top));
+ top = make_unique();
+ break;
+ case '>':
+ case ')':
+ if (stack.size() == 0) {
+ throw std::invalid_argument("Unbalanced brackets");
+ } else if (c == '>' && top->type != group_types::symbol) {
+ throw std::invalid_argument("Unexpected '>' in pattern");
+ } else if (c == ')' && top->type != group_types::literal) {
+ throw std::invalid_argument("Unexpected ')' in pattern");
+ }
+ last = top->produce();
+ top = std::move(stack.top());
+ stack.pop();
+ top->add(std::move(last));
+ break;
+ case '|':
+ top->split();
+ break;
+ case '!':
+ if (top->type == group_types::symbol) {
+ top->wrap(wrappers::capitalizer);
+ } else {
+ top->add(c);
+ }
+ break;
+ case '~':
+ if (top->type == group_types::symbol) {
+ top->wrap(wrappers::reverser);
+ } else {
+ top->add(c);
+ }
+ break;
+ default:
+ top->add(c);
+ break;
+ }
+ }
+
+ if (stack.size() != 0) {
+ throw std::invalid_argument("Missing closing bracket");
+ }
+
+ std::unique_ptr g = top->produce();
+ if (collapse_triples) {
+ g = make_unique(std::move(g));
+ }
+ add(std::move(g));
+}
+
+
+Generator::Group::Group(group_types_t type_) :
+ type(type_)
+{
+}
+
+void Generator::Group::add(std::unique_ptr&& g)
+{
+ while (!wrappers.empty()) {
+ switch (wrappers.top()) {
+ case reverser:
+ g = make_unique(std::move(g));
+ break;
+ case capitalizer:
+ g = make_unique(std::move(g));
+ break;
+ }
+ wrappers.pop();
+ }
+ if (set.size() == 0) {
+ set.push_back(make_unique());
+ }
+ set.back()->add(std::move(g));
+}
+
+void Generator::Group::add(char c)
+{
+ std::string value(1, c);
+ std::unique_ptr g = make_unique();
+ g->add(make_unique(value));
+ Group::add(std::move(g));
+}
+
+std::unique_ptr Generator::Group::produce()
+{
+ switch (set.size()) {
+ case 0:
+ return make_unique("");
+ case 1:
+ return std::move(*set.begin());
+ default:
+ return make_unique(std::move(set));
+ }
+}
+
+void Generator::Group::split()
+{
+ if (set.size() == 0) {
+ set.push_back(make_unique());
+ }
+ set.push_back(make_unique());
+}
+
+void Generator::Group::wrap(wrappers_t type)
+{
+ wrappers.push(type);
+}
+
+Generator::GroupSymbol::GroupSymbol() :
+ Group(group_types::symbol)
+{
+}
+
+void Generator::GroupSymbol::add(char c)
+{
+ std::string value(1, c);
+ std::unique_ptr g = make_unique();
+ try {
+ static const auto& symbols = SymbolMap();
+ for (const auto& s : symbols.at(value)) {
+ g->add(make_unique(s));
+ }
+ } catch (const std::out_of_range&) {
+ g->add(make_unique(value));
+ }
+ Group::add(std::move(g));
+}
+
+Generator::GroupLiteral::GroupLiteral() :
+ Group(group_types::literal)
+{
+}
+
+std::wstring towstring(const std::string & s)
+{
+ const char *cs = s.c_str();
+ const size_t wn = std::mbsrtowcs(nullptr, &cs, 0, nullptr);
+
+ if (wn == static_cast(-1)) {
+ return L"";
+ }
+
+ std::vector buf(wn);
+ cs = s.c_str();
+ const size_t wn_again = std::mbsrtowcs(buf.data(), &cs, wn, nullptr);
+
+ if (wn_again == static_cast(-1)) {
+ return L"";
+ }
+
+ return std::wstring(buf.data(), wn);
+}
+
+std::string tostring(const std::wstring & s)
+{
+ const wchar_t *cs = s.c_str();
+ const size_t wn = std::wcsrtombs(nullptr, &cs, 0, nullptr);
+
+ if (wn == static_cast(-1)) {
+ return "";
+ }
+
+ std::vector buf(wn);
+ const size_t wn_again = std::wcsrtombs(buf.data(), &cs, wn, nullptr);
+
+ if (wn_again == static_cast(-1)) {
+ return "";
+ }
+
+ return std::string(buf.data(), wn);
+}
diff --git a/fantasyname/namegen.h b/fantasyname/namegen.h
new file mode 100644
index 000000000..c90fa30e0
--- /dev/null
+++ b/fantasyname/namegen.h
@@ -0,0 +1,268 @@
+/**
+ *
+ * @file A fantasy name generator library.
+ * @version 1.0.1
+ * @license Public Domain
+ * @author German M. Bravo (Kronuz)
+ *
+ * This library is designed after the RinkWorks Fantasy Name Generator.
+ * @see http://www.rinkworks.com/namegen/
+ *
+ * @example
+ * NameGen::Generator generator("sV'i");
+ * generator.toString(); // Returns a new name each call with produce()
+ * // => "entheu'loaf"
+ *
+ * ## Pattern Syntax
+ *
+ * The compile() function creates a name generator based on an input
+ * pattern. The letters s, v, V, c, B, C, i, m, M, D, and d represent
+ * different types of random replacements. Everything else is produced
+ * literally.
+ *
+ * s - generic syllable
+ * v - vowel
+ * V - vowel or vowel combination
+ * c - consonant
+ * B - consonant or consonant combination suitable for beginning a word
+ * C - consonant or consonant combination suitable anywhere in a word
+ * i - insult
+ * m - mushy name
+ * M - mushy name ending
+ * D - consonant suited for a stupid person's name
+ * d - syllable suited for a stupid person's name (begins with a vowel)
+ *
+ * All characters between parenthesis () are produced literally. For
+ * example, the pattern "s(dim)", produces a random generic syllable
+ * followed by "dim".
+ *
+ * Characters between angle brackets <> produce patterns from the table
+ * above. Imagine the entire pattern is wrapped in one of these.
+ *
+ * In both types of groupings, a vertical bar | denotes a random
+ * choice. Empty groups are allowed. For example, "(foo|bar)" produces
+ * either "foo" or "bar". The pattern "" produces a constant,
+ * vowel, or nothing at all.
+ *
+ * An exclamation point ! means to capitalize the component that
+ * follows it. For example, "!(foo)" will produce "Foo" and "v!s" will
+ * produce a lowercase vowel followed by a capitalized syllable, like
+ * "eRod".
+ *
+ * A tilde ~ means to reverse the letters of the component that
+ * follows it. For example, "~(foo)" will produce "oof". To reverse an
+ * entire template, wrap it in brackets. For example, to reverse
+ * "sV'i" as a whole use "~". The template "~sV'i" will only
+ * reverse the initial syllable.
+ *
+ * ## Internals
+ *
+ * A name generator is anything with a toString() method, including,
+ * importantly, strings themselves. The generator constructors
+ * (Random, Sequence) perform additional optimizations when *not* used
+ * with the `new` keyword: they may pass through a provided generator,
+ * combine provided generators, or even return a simple string.
+ *
+ * New pattern symbols added to Generator.symbols will automatically
+ * be used by the compiler.
+ */
+
+#pragma once
+
+#include // for size_t
+#include // for wstring
+#include // for unique_ptr
+#include // for stack
+#include // for string
+#include // for unordered_map
+#include // for vector
+
+
+namespace NameGen {
+
+// Middle Earth
+#define MIDDLE_EARTH "(bil|bal|ban|hil|ham|hal|hol|hob|wil|me|or|ol|od|gor|for|fos|tol|ar|fin|ere|leo|vi|bi|bren|thor)(|go|orbis|apol|adur|mos|ri|i|na|ole|n)(|tur|axia|and|bo|gil|bin|bras|las|mac|grim|wise|l|lo|fo|co|ra|via|da|ne|ta|y|wen|thiel|phin|dir|dor|tor|rod|on|rdo|dis)"
+
+// Japanese Names (Constrained)
+#define JAPANESE_NAMES_CONSTRAINED "(aka|aki|bashi|gawa|kawa|furu|fuku|fuji|hana|hara|haru|hashi|hira|hon|hoshi|ichi|iwa|kami|kawa|ki|kita|kuchi|kuro|marui|matsu|miya|mori|moto|mura|nabe|naka|nishi|no|da|ta|o|oo|oka|saka|saki|sawa|shita|shima|i|suzu|taka|take|to|toku|toyo|ue|wa|wara|wata|yama|yoshi|kei|ko|zawa|zen|sen|ao|gin|kin|ken|shiro|zaki|yuki|asa)(||||||||||bashi|gawa|kawa|furu|fuku|fuji|hana|hara|haru|hashi|hira|hon|hoshi|chi|wa|ka|kami|kawa|ki|kita|kuchi|kuro|marui|matsu|miya|mori|moto|mura|nabe|naka|nishi|no|da|ta|o|oo|oka|saka|saki|sawa|shita|shima|suzu|taka|take|to|toku|toyo|ue|wa|wara|wata|yama|yoshi|kei|ko|zawa|zen|sen|ao|gin|kin|ken|shiro|zaki|yuki|sa)"
+
+// Japanese Names (Diverse)
+#define JAPANESE_NAMES_DIVERSE "(a|i|u|e|o|||||)(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)))(|||n)"
+
+// Chinese Names
+#define CHINESE_NAMES "(zh|x|q|sh|h)(ao|ian|uo|ou|ia)(|(l|w|c|p|b|m)(ao|ian|uo|ou|ia)(|n)|-(l|w|c|p|b|m)(ao|ian|uo|ou|ia)(|(d|j|q|l)(a|ai|iu|ao|i)))"
+
+// Greek Names
+#define GREEK_NAMES "(tia)|s(os)|Bc(ios)|Bv(ios|os)>"
+
+// Hawaiian Names (1)
+#define HAWAIIAN_NAMES_1 "((h|k|l|m|n|p|w|')|)(a|e|i|o|u)((h|k|l|m|n|p|w|')|)(a|e|i|o|u)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)"
+
+// Hawaiian Names (2)
+#define HAWAIIAN_NAMES_2 "((h|k|l|m|n|p|w|)(a|e|i|o|u|a'|e'|i'|o'|u'|ae|ai|ao|au|oi|ou|eu|ei)(k|l|m|n|p|)|)(h|k|l|m|n|p|w|)(a|e|i|o|u|a'|e'|i'|o'|u'|ae|ai|ao|au|oi|ou|eu|ei)(k|l|m|n|p|)"
+
+// Old Latin Place Names
+#define OLD_LATIN_PLACE_NAMES "sv(nia|lia|cia|sia)"
+
+// Dragons (Pern)
+#define DRAGONS_PERN "<|>>(th)"
+
+// Dragon Riders
+#define DRAGON_RIDERS "c'"
+
+// Pokemon
+#define POKEMON "v(mon|chu|zard|rtle)"
+
+// Fantasy (Vowels, R, etc.)
+#define FANTASY_VOWELS_R "(|(|s|h|ty|ph|r))(i|ae|ya|ae|eu|ia|i|eo|ai|a)(lo|la|sri|da|dai|the|sty|lae|due|li|lly|ri|na|ral|sur|rith)(|(su|nu|sti|llo|ria|))(|(n|ra|p|m|lis|cal|deu|dil|suir|phos|ru|dru|rin|raap|rgue))"
+
+// Fantasy (S, A, etc.)
+#define FANTASY_S_A "(cham|chan|jisk|lis|frich|isk|lass|mind|sond|sund|ass|chad|lirt|und|mar|lis|il|)(jask|ast|ista|adar|irra|im|ossa|assa|osia|ilsa|)(|(an|ya|la|sta|sda|sya|st|nya))"
+
+// Fantasy (H, L, etc.)
+#define FANTASY_H_L "(ch|ch't|sh|cal|val|ell|har|shar|shal|rel|laen|ral|jh't|alr|ch|ch't|av)(|(is|al|ow|ish|ul|el|ar|iel))(aren|aeish|aith|even|adur|ulash|alith|atar|aia|erin|aera|ael|ira|iel|ahur|ishul)"
+
+// Fantasy (N, L, etc.)
+#define FANTASY_N_L "(ethr|qil|mal|er|eal|far|fil|fir|ing|ind|il|lam|quel|quar|quan|qar|pal|mal|yar|um|ard|enn|ey)(|(|on|us|un|ar|as|en|ir|ur|at|ol|al|an))(uard|wen|arn|on|il|ie|on|iel|rion|rian|an|ista|rion|rian|cil|mol|yon)"
+
+// Fantasy (K, N, etc.)
+#define FANTASY_K_N "(taith|kach|chak|kank|kjar|rak|kan|kaj|tach|rskal|kjol|jok|jor|jad|kot|kon|knir|kror|kol|tul|rhaok|rhak|krol|jan|kag|ryr)(|in|or|an|ar|och|un|mar|yk|ja|arn|ir|ros|ror)(|(mund|ard|arn|karr|chim|kos|rir|arl|kni|var|an|in|ir|a|i|as))"
+
+// Fantasy (J, G, Z, etc.)
+#define FANTASY_J_G_Z "(aj|ch|etz|etzl|tz|kal|gahn|kab|aj|izl|ts|jaj|lan|kach|chaj|qaq|jol|ix|az|biq|nam)(|(|aw|al|yes|il|ay|en|tom||oj|im|ol|aj|an|as))(aj|am|al|aqa|ende|elja|ich|ak|ix|in|ak|al|il|ek|ij|os|al|im)"
+
+// Fantasy (K, J, Y, etc.)
+#define FANTASY_K_J_Y "(yi|shu|a|be|na|chi|cha|cho|ksa|yi|shu)(th|dd|jj|sh|rr|mk|n|rk|y|jj|th)(us|ash|eni|akra|nai|ral|ect|are|el|urru|aja|al|uz|ict|arja|ichi|ural|iru|aki|esh)"
+
+// Fantasy (S, E, etc.)
+#define FANTASY_S_E "(syth|sith|srr|sen|yth|ssen|then|fen|ssth|kel|syn|est|bess|inth|nen|tin|cor|sv|iss|ith|sen|slar|ssil|sthen|svis|s|ss|s|ss)(|(tys|eus|yn|of|es|en|ath|elth|al|ell|ka|ith|yrrl|is|isl|yr|ast|iy))(us|yn|en|ens|ra|rg|le|en|ith|ast|zon|in|yn|ys)"
+
+
+class Generator
+{
+ typedef enum wrappers {
+ capitalizer,
+ reverser
+ } wrappers_t;
+
+ typedef enum group_types {
+ symbol,
+ literal
+ } group_types_t;
+
+
+ class Group {
+ std::stack wrappers;
+ std::vector> set;
+
+ public:
+ group_types_t type;
+
+ Group(group_types_t type_);
+
+ std::unique_ptr produce();
+ void split();
+ void wrap(wrappers_t type);
+ void add(std::unique_ptr&& g);
+
+ virtual void add(char c);
+ };
+
+
+ class GroupSymbol : public Group {
+ public:
+ GroupSymbol();
+ void add(char c);
+ };
+
+
+ class GroupLiteral : public Group {
+ public:
+ GroupLiteral();
+ };
+
+protected:
+ std::vector> generators;
+
+public:
+ static const std::unordered_map>& SymbolMap();
+
+ Generator();
+ Generator(const std::string& pattern, bool collapse_triples=true);
+ Generator(std::vector>&& generators_);
+
+ virtual ~Generator() = default;
+
+ virtual size_t combinations();
+ virtual size_t min();
+ virtual size_t max();
+ virtual std::string toString();
+
+ void add(std::unique_ptr&& g);
+};
+
+
+class Random : public Generator
+{
+public:
+ Random();
+ Random(std::vector>&& generators_);
+
+ size_t combinations();
+ size_t min();
+ size_t max();
+ std::string toString();
+};
+
+
+class Sequence : public Generator
+{
+public:
+ Sequence();
+ Sequence(std::vector>&& generators_);
+};
+
+
+class Literal : public Generator
+{
+ std::string value;
+
+public:
+ Literal(const std::string& value_);
+
+ size_t combinations();
+ size_t min();
+ size_t max();
+ std::string toString();
+};
+
+
+class Reverser : public Generator {
+public:
+ Reverser(std::unique_ptr&& g);
+
+ std::string toString();
+};
+
+
+class Capitalizer : public Generator
+{
+public:
+ Capitalizer(std::unique_ptr&& g);
+
+ std::string toString();
+};
+
+
+class Collapser : public Generator
+{
+public:
+ Collapser(std::unique_ptr&& g);
+
+ std::string toString();
+};
+
+}
+
+std::wstring towstring(const std::string& s);
+std::string tostring(const std::wstring& s);