Improvements in the map and name generators

- Implement a way to escape special characters {|} in the CFG generator
- Eliminate null pointers to name generators
- Invalid generators now throw exceptions
- Name generating rules for different terrain elements can now be specified
  (The defaults in english.cfg are still used)
This commit is contained in:
Spixi 2016-07-17 21:53:02 +02:00 committed by Celtic Minstrel
parent a8f625fc8a
commit f96f86245e
17 changed files with 415 additions and 269 deletions

View file

@ -155,7 +155,7 @@ Version 1.13.4+dev:
and [deprecated_message] tags now use this.
* New wesnoth.name_generator function builds a name generator and returns
it as a callable userdata. Both the original Markov chain generator
and the new context free gramamr generator are supported
and the new context free grammar generator are supported
* WML tables defined in Lua now accept string keys with array values
(where "array" is a table whose keys are all integers). This joins
the elements of the array with commas and produces a single string
@ -279,6 +279,9 @@ Version 1.13.4+dev:
* pair() function that produces a key-value pair suitable for
passing to tomap() - this also means key-value pairs are now
serializable (relevant in FormulaAI)
* Map generator engine:
* makes now use of the new context free grammar name generator
* ported name generation from english.cfg to [naming]
* Bugfixes:
* Dice operator is now synced (where possible)
* Modulus (%) operator now works on decimal numbers

View file

@ -248,8 +248,8 @@ suffix=men|drum|tor|num|lad|de|ak|lol|dum|tam|nur|dium|deum|bil|rook|relm|dium|n
#enddef
#define VILLAGE_NAMES
male_names= _ "Bal,Cam,Corn,Del,Earl,El,Fox,Fren,Gel,Hel,Hex,Hol,Hox,Il,Kin,Nam,Nes,New,Ol,Old,Olf,Oul,Ox,Rock,Rook,Sal,Sam,Sed,Sel,Sen,Sil,Tal,Water,Wet,York"
name_generator= _ <<
base_names= _ "Bal,Cam,Corn,Del,Earl,El,Fox,Fren,Gel,Hel,Hex,Hol,Hox,Il,Kin,Nam,Nes,New,Ol,Old,Olf,Oul,Ox,Rock,Rook,Sal,Sam,Sed,Sel,Sen,Sil,Tal,Water,Wet,York"
base_name_generator= _ <<
main={prefix}{middle}{suffix}
prefix=B|C|D|E|F|Fr|Wat|G|H|K|N|O|R|S|T|W|Y|Ro
middle=a|e|o|u|i

View file

@ -19,26 +19,6 @@ Night: +25% Damage"
Day: 25% Damage
Night: 25% Damage"
#naming of terrain features
bridge_name= _ "$name|s Bridge,$name|s Crossing"
road_name= _ "$name|s Highway,$name|s Pass,Path of $name"
river_name= _ "$name River,River $name"
forest_name= _ "$name Forest,$name|s Forest"
lake_name= _ "$name Lake"
mountain_name= _ "$name|s Peak,Mount $name"
swamp_name= _ "$name|s Swamp,$name|marsh,$name|fen"
village_name= _ "$name|bury,$name|ham,$name|ton,$name|bury"
village_name_lake= _ "$name|harbor,$name|port,$lake|port,$lake|harbor"
village_name_river= _ "$name|ham,$name|ford,$name|cross,$river|ford,$river|cross,$name on river"
village_name_river_bridge= _ "$river|bridge,$river|bridge,$river|bridge,$name|ham,$name|bridge,$bridge|ham,$bridge|ton"
village_name_grassland= _ "$name|ham,$name|ton,$name|field"
village_name_forest= _ "$name|ham,$name|ton,$name|wood,$name Forest,$forest|wood,$forest|ham,$forest|ton"
village_name_hill= _ "$name|ham,$name|bury,$name|ton,$name|hill,$name|crest"
village_name_mountain= _ "$mountain|mont,$mountain|cliff,$mountain|bury,$mountain|ham"
village_name_mountain_anonymous= _ "$name|ham,$name|bury,$name|ton,$name|mont,$name|mont,$name|cliff,$name|cliff"
village_name_road= _ "$road|s Rest,$road|s Waypoint,$road|bury,$road|ham,$name|bury,$name|ham"
village_name_swamp= _ "$name|bury,$name|ham,$name|ton,$swamp|bury,$swamp|ham,$swamp|ton,"
#ranges
range_melee= _ "melee"
range_ranged= _ "ranged"
@ -51,3 +31,28 @@ Night: 25% Damage"
type_cold= _ "cold"
type_arcane= _ "arcane"
[/language]
#default naming of terrain features
[naming]
bridge_name_generator= _ << main=$base{!}s Bridge|$base{!}s Crossing >>
road_name_generator= _ << main=$base{!}s Highway|$base{!}s Pass|Path of $base >>
river_name_generator= _ << main=$base River|River $base >>
forest_name_generator= _ << main=$base Forest|$base{!}s Forest >>
lake_name_generator= _ << main=$base{!} Lake >>
mountain_name_generator= _ << main=$base{!}s Peak|Mount $base >>
swamp_name_generator= _ << main=$base{!}s Swamp|base{!}marsh|$base{!}fen >>
[/naming]
[village_naming]
name_generator= _ << main = $base{!}bury|$base{!}ham|$base{!}ton|$base{!}bury >>
lake_name_generator= _ << main=$base{!}harbor|$base{!}port|$lake{!}port|$lake{!}harbor >>
river_name_generator= _ << main=$base{!}ham|$base{!}ford|$base{!}cross|$river{!}ford|$river{!}cross|$base on $river >>
bridge_name_generator= _ << main=$river{!}bridge|$river{!}bridge|$river{!}bridge|$base{!}ham|$base{!}bridge|$bridge{!}ham|$bridge{!}ton >>
grassland_name_generator= _ << main=$base{!}ham|$base{!}ton|$base{!}field >>
forest_name_generator= _ << main=$base{!}ham|$base{!}ton|$base{!}wood|$base Forest|$forest{!}wood|$forest{!}ham|$forest{!}ton >>
hill_name_generator= _ << main=$base{!}ham|$base{!}bury|$base{!}ton|$base{!}hill|$base{!}crest >>
mountain_name_generator= _ << main=$mountain{!}mont|$mountain{!}cliff|$mountain{!}bury|$mountain{!}ham >>
mountain_anonymous_name_generator= _ << "main=$base{!}ham|$base{!}bury|$base{!}ton|$base{!}mont|$base{!}mont|$base{!}cliff|$base{!}cliff >>
road_name_generator= _ << main=$road{!}s Rest|$road{!}s Waypoint|$road{!}bury|$road{!}ham|$base{!}bury|$base{!}ham >>
swamp_name_generator= _ << main=$base{!}bury|$base{!}ham|$base}ton|$swamp{!}bury|$swamp{!}ham|$swamp{!}ton >>
[/village_naming]

View file

@ -986,6 +986,7 @@ set(wesnoth-main_SRC
utils/sha1.cpp
utils/context_free_grammar_generator.cpp
utils/markov_generator.cpp
utils/name_generator_factory.cpp
variable.cpp
variable_info.cpp
whiteboard/action.cpp

View file

@ -558,6 +558,7 @@ wesnoth_sources = Split("""
utils/sha1.cpp
utils/context_free_grammar_generator.cpp
utils/markov_generator.cpp
utils/name_generator_factory.cpp
variable_info.cpp
variable.cpp
whiteboard/action.cpp

View file

@ -27,21 +27,22 @@ static bool two_dots(char a, char b) { return a == '.' && b == '.'; }
namespace utils {
template <typename T>
class string_map_variable_set : public variable_set
{
public:
string_map_variable_set(const string_map& map) : map_(map) {}
string_map_variable_set(const std::map<std::string,T>& map) : map_(map) {}
virtual config::attribute_value get_variable_const(const std::string &key) const
{
config::attribute_value val;
const string_map::const_iterator itor = map_.find(key);
const auto itor = map_.find(key);
if (itor != map_.end())
val = itor->second;
return val;
}
private:
const string_map& map_;
const std::map<std::string,T>& map_;
};
}
@ -213,7 +214,13 @@ namespace utils {
std::string interpolate_variables_into_string(const std::string &str, const string_map * const symbols)
{
string_map_variable_set set(*symbols);
auto set = string_map_variable_set<t_string>(*symbols);
return do_interpolation(str, set);
}
std::string interpolate_variables_into_string(const std::string &str, const std::map<std::string,std::string> * const symbols)
{
auto set = string_map_variable_set<std::string>(*symbols);
return do_interpolation(str, set);
}

View file

@ -42,6 +42,7 @@ inline bool might_contain_variables(const std::string &str)
* is nullptr, then game event variables will be used instead.
*/
std::string interpolate_variables_into_string(const std::string &str, const string_map * const symbols);
std::string interpolate_variables_into_string(const std::string &str, const std::map<std::string,std::string> * const symbols);
std::string interpolate_variables_into_string(const std::string &str, const variable_set& variables);
/**

View file

@ -30,9 +30,9 @@
#include "util.hpp"
#include "wml_exception.hpp"
#include "formula/string_utils.hpp"
#include "utils/context_free_grammar_generator.hpp"
#include "utils/markov_generator.hpp"
#include "utils/name_generator_factory.hpp"
#include <SDL.h>
#include "game_config_manager.hpp"
#include "seed_rng.hpp"
static lg::log_domain log_mapgen("mapgen");
@ -40,13 +40,15 @@ static lg::log_domain log_mapgen("mapgen");
#define LOG_NG LOG_STREAM(info, log_mapgen)
default_map_generator_job::default_map_generator_job()
: rng_(seed_rng::next_seed())
: rng_(seed_rng::next_seed()),
game_config_(game_config_manager::get()->game_config())
{
}
default_map_generator_job::default_map_generator_job(uint32_t seed)
: rng_(seed)
: rng_(seed),
game_config_(game_config_manager::get()->game_config())
{
}
@ -70,7 +72,7 @@ typedef map_location location;
* 0 indicates no island.
*/
height_map default_map_generator_job::generate_height_map(size_t width, size_t height,
size_t iterations, size_t hill_size,
size_t iterations, size_t hill_size,
size_t island_size, size_t island_off_center)
{
height_map res(width, std::vector<int>(height,0));
@ -119,9 +121,9 @@ height_map default_map_generator_job::generate_height_map(size_t width, size_t h
bool is_valley = false;
int x1 = island_size > 0 ? center_x - island_size + (rng_()%(island_size*2)) :
int(rng_()%width);
int(rng_()%width);
int y1 = island_size > 0 ? center_y - island_size + (rng_()%(island_size*2)) :
int(rng_()%height);
int(rng_()%height);
// We have to check whether this is actually a valley
if(island_size != 0) {
@ -273,7 +275,7 @@ bool default_map_generator_job::generate_river_internal(const height_map& height
// Generate the river
for(std::vector<location>::const_iterator i = river.begin();
i != river.end(); ++i) {
i != river.end(); ++i) {
terrain[i->x][i->y] = t_translation::SHALLOW_WATER;
}
@ -382,7 +384,7 @@ namespace {
struct road_path_calculator : pathfind::cost_calculator
{
road_path_calculator(const terrain_map& terrain, const config& cfg, int seed) :
calls(0),
calls(0),
map_(terrain),
cfg_(cfg),
// Find out how windy roads should be.
@ -521,7 +523,7 @@ static int rank_castle_location(int x, int y, const is_valid_terrain& valid_terr
const int y_from_border = std::min<int>(y - min_y,max_y - y);
const int border_ranking = min_distance - std::min<int>(x_from_border,y_from_border) +
min_distance - x_from_border - y_from_border;
min_distance - x_from_border - y_from_border;
int current_ranking = border_ranking*2 + avg_distance*10 + lowest_distance*10;
static const int num_nearby_locations = 11*11;
@ -602,35 +604,6 @@ static map_location place_village(const t_translation::t_map& map,
return best_loc;
}
std::string default_map_generator_job::generate_name(boost::shared_ptr<name_generator>& name_generator, const std::string& id,
std::string* base_name, utils::string_map* additional_symbols)
{
const std::vector<std::string>& options = utils::split(string_table[id].str());
if(options.empty() == false) {
const size_t choice = rng_()%options.size();
LOG_NG << "calling name generator...\n";
const std::string& name = name_generator->generate();
LOG_NG << "name generator returned '" << name << "'\n";
if(base_name != nullptr) {
*base_name = name;
}
LOG_NG << "assigned base name..\n";
utils::string_map table;
if(additional_symbols == nullptr) {
additional_symbols = &table;
}
LOG_NG << "got additional symbols\n";
(*additional_symbols)["name"] = name;
LOG_NG << "interpolation variables into '" << options[choice] << "'\n";
return utils::interpolate_variables_into_string(options[choice], additional_symbols);
}
return "";
}
// "flood fill" a tile name to adjacent tiles of certain terrain
static void flood_name(const map_location& start, const std::string& name, std::map<map_location,std::string>& tile_names,
const t_translation::t_match& tile_types, const terrain_map& terrain,
@ -665,8 +638,8 @@ namespace {
// the configuration file should contain a number of [height] tags:
// [height]
// height=n
// terrain=x
// height=n
// terrain=x
// [/height]
// These should be in descending order of n.
// They are checked sequentially, and if height is greater than n for that tile,
@ -736,7 +709,7 @@ bool terrain_converter::convert_terrain(const t_translation::t_terrain & terrain
const int height, const int temperature) const
{
return std::find(from.begin(),from.end(),terrain) != from.end() && height >= min_height && height <= max_height &&
temperature >= min_temp && temperature <= max_temp && to != t_translation::NONE_TERRAIN;
temperature >= min_temp && temperature <= max_temp && to != t_translation::NONE_TERRAIN;
}
t_translation::t_terrain terrain_converter::convert_to() const
@ -747,8 +720,8 @@ t_translation::t_terrain terrain_converter::convert_to() const
} // end anon namespace
std::string default_map_generator_job::default_generate_map(size_t width, size_t height, size_t island_size, size_t island_off_center,
size_t iterations, size_t hill_size,
size_t max_lakes, size_t nvillages, size_t castle_size, size_t nplayers, bool roads_between_castles,
size_t iterations, size_t hill_size,
size_t max_lakes, size_t nvillages, size_t castle_size, size_t nplayers, bool roads_between_castles,
std::map<map_location,std::string>* labels, const config& cfg)
{
log_scope("map generation");
@ -756,6 +729,13 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
// Odd widths are nasty
VALIDATE(is_even(width), _("Random maps with an odd width aren't supported."));
/** Try to find configuration for castles. */
const config &castle_config = cfg.child("castle");
if (!castle_config) {
LOG_NG << "Could not find castle configuration\n";
return std::string();
}
int ticks = SDL_GetTicks();
// Find out what the 'flatland' on this map is, i.e. grassland.
@ -781,21 +761,45 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
LOG_NG << "done generating height map...\n";
LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
config naming = cfg.child_or_empty("naming");
config naming;
naming.append_children(game_config_,"naming");
if(cfg.has_child("naming"))
naming.append_children(cfg,"naming");
naming.merge_children("naming");
naming = naming.child_or_empty("naming");
// If the [naming] child is empty, we cannot provide good names.
std::map<map_location,std::string>* misc_labels = naming.empty() ? nullptr : labels;
boost::shared_ptr<name_generator> name_generator;
if(naming.has_attribute("name_generator")) {
name_generator.reset(new context_free_grammar_generator(naming["name_generator"]));
if(!name_generator->is_valid()) {
name_generator.reset();
std::shared_ptr<name_generator> base_name_generator;
std::shared_ptr<name_generator> river_name_generator;
std::shared_ptr<name_generator> lake_name_generator;
std::shared_ptr<name_generator> road_name_generator;
std::shared_ptr<name_generator> bridge_name_generator;
std::shared_ptr<name_generator> mountain_name_generator;
std::shared_ptr<name_generator> forest_name_generator;
std::shared_ptr<name_generator> swamp_name_generator;
if(misc_labels != nullptr) {
name_generator_factory base_generator_factory{ naming, {"male", "base", "bridge", "road", "river", "forest", "lake", "mountain", "swamp"} };
naming.get_old_attribute("base_names", "male_names", "[naming]male_names= is deprecated, use base_names= instead");
//Due to the attribute detection feature of the factory we also support male_name_generator= but keep it undocumented.
if(naming.has_attribute("base_names")) {
base_name_generator = base_generator_factory.get_name_generator( (naming.has_attribute("base_names") || naming.has_attribute("base_name_generator")) ? "base" : "male" );
}
}
config::attribute_value markov_list = naming.get_old_attribute("names", "male_names",
"[naming]male_names is deprecated, use names instead");
if(!markov_list.blank()) {
name_generator.reset(new markov_generator(utils::split(markov_list), naming["markov_chain_size"], 12));
else {
base_name_generator = base_generator_factory.get_name_generator("male");
}
river_name_generator = base_generator_factory.get_name_generator("river");
lake_name_generator = base_generator_factory.get_name_generator("lake");
road_name_generator = base_generator_factory.get_name_generator("road");
bridge_name_generator = base_generator_factory.get_name_generator("bridge");
mountain_name_generator = base_generator_factory.get_name_generator("mountain");
forest_name_generator = base_generator_factory.get_name_generator("forest");
swamp_name_generator = base_generator_factory.get_name_generator("swamp");
}
std::vector<terrain_height_mapper> height_conversion;
@ -809,7 +813,7 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
for(x = 0; x != heights.size(); ++x) {
for(y = 0; y != heights[x].size(); ++y) {
for(std::vector<terrain_height_mapper>::const_iterator i = height_conversion.begin();
i != height_conversion.end(); ++i) {
i != height_conversion.end(); ++i) {
if(i->convert_terrain(heights[x][y])) {
terrain[x][y] = i->convert_to();
break;
@ -850,9 +854,9 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
terrain, x, y, cfg["river_frequency"]);
if(river.empty() == false && misc_labels != nullptr) {
std::string base_name;
std::string base_name = base_name_generator->generate();
LOG_NG << "generating name for river...\n";
const std::string& name = generate_name(name_generator,"river_name",&base_name);
const std::string& name = river_name_generator->generate({{"base", base_name}});
LOG_NG << "named river '" << name << "'\n";
size_t name_frequency = 20;
for(std::vector<location>::const_iterator r = river.begin(); r != river.end(); ++r) {
@ -875,8 +879,8 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
if(res && misc_labels != nullptr) {
bool touches_other_lake = false;
std::string base_name;
const std::string& name = generate_name(name_generator,"lake_name",&base_name);
std::string base_name = base_name_generator->generate();
const std::string& name = lake_name_generator->generate({{"base", base_name}});
std::set<location>::const_iterator i;
@ -968,13 +972,6 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
LOG_NG << "placing castles...\n";
/** Try to find configuration for castles. */
const config &castle_config = cfg.child("castle");
if (!castle_config) {
LOG_NG << "Could not find castle configuration\n";
return std::string();
}
/*
* Castle configuration tag contains a 'valid_terrain' attribute which is a
* list of terrains that the castle may appear on.
@ -1089,8 +1086,8 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
// Search a path out for the road
pathfind::plain_route rt = pathfind::a_star_search(src, dst, 10000.0, &calc, width, height);
std::string road_base_name;
const std::string& name = generate_name(name_generator, "road_name", &road_base_name);
std::string road_name = base_name_generator->generate();
const std::string& name = road_name_generator->generate({{"base", road_name}});
const int name_frequency = 20;
int name_count = 0;
@ -1154,8 +1151,8 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
if(misc_labels != nullptr && on_bridge == false) {
on_bridge = true;
std::string bridge_base_name;
const std::string& name = generate_name(name_generator, "bridge_name", &bridge_base_name);
std::string bridge_base_name = base_name_generator->generate();
const std::string& name = bridge_name_generator->generate({{"base", bridge_base_name}});
const location loc(x - width / 3, y-height/3);
misc_labels->insert(std::pair<map_location,std::string>(loc,name));
bridge_names.insert(std::pair<location,std::string>(loc, bridge_base_name)); //add to use for village naming
@ -1186,7 +1183,7 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
terrain[x][y] = letter;
const location loc(x - width / 3, y - height / 3); //add to use for village naming
road_names.insert(std::pair<location,std::string>(loc, road_base_name));
road_names.insert(std::pair<location,std::string>(loc, road_name));
}
}
}
@ -1221,7 +1218,7 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
if(labels != nullptr) {
labels->erase(location(x-width/3,y-height/3));
for (size_t i = 0; i < castle_size - 1; i++) {
labels->erase(location(x+castles[i][0]-width/3,
labels->erase(location(x+castles[i][0]-width/3,
y+castles[i][1]-height/3));
}
@ -1248,7 +1245,8 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
//name every 15th mountain
if ((rng_()%15) == 0) {
for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
name = generate_name(name_generator, "mountain_name", &base_name);
base_name = base_name_generator->generate();
name = mountain_name_generator->generate({{"base", base_name}});
}
misc_labels->insert(std::pair<map_location, std::string>(loc, name));
mountain_names.insert(std::pair<location, std::string>(loc, base_name));
@ -1259,7 +1257,8 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
const std::map<location, std::string>::const_iterator forest_name = forest_names.find(loc);
if(forest_name == forest_names.end()) {
for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
name = generate_name(name_generator, "forest_name", &base_name);
base_name = base_name_generator->generate();
name = forest_name_generator->generate({{"base", base_name}});
}
forest_names.insert(std::pair<location, std::string>(loc, base_name));
// name all connected forest tiles accordingly
@ -1271,7 +1270,8 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
const std::map<location, std::string>::const_iterator swamp_name = swamp_names.find(loc);
if(swamp_name == swamp_names.end()) {
for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
name = generate_name(name_generator, "swamp_name", &base_name);
base_name = base_name_generator->generate();
name = swamp_name_generator->generate({{"base", base_name}});
}
swamp_names.insert(std::pair<location, std::string>(loc, base_name));
// name all connected swamp tiles accordingly
@ -1284,24 +1284,6 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
if (nvillages > 0)
{
config naming_cfg = cfg.child_or_empty("village_naming");
// If the [village_naming] child is empty, we cannot provide good names.
std::map<map_location,std::string>* village_labels = naming_cfg.empty() ? nullptr : labels;
// Specify "class" here because we also have a local variable with the same name
boost::shared_ptr<class name_generator> village_names_generator;
if(naming_cfg.has_attribute("name_generator")) {
village_names_generator.reset(new context_free_grammar_generator(naming["name_generator"]));
if(!village_names_generator->is_valid()) {
village_names_generator.reset();
}
}
config::attribute_value markov_list = naming_cfg.get_old_attribute("names", "male_names",
"[village_naming]male_names is deprecated, use names instead");
if(!markov_list.blank()) {
village_names_generator.reset(new markov_generator(utils::split(markov_list), naming["markov_chain_size"], 12));
}
// First we work out the size of the x and y distance between villages
const size_t tiles_per_village = ((width*height)/9)/nvillages;
size_t village_x = 1, village_y = 1;
@ -1320,6 +1302,16 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
std::set<std::string> used_names;
tcode_list_cache adj_liked_cache;
config village_naming;
village_naming.append_children(game_config_,"village_naming");
if(cfg.has_child("village_naming"))
village_naming.append_children(cfg,"village_naming");
village_naming.merge_children("village_naming");
village_naming = village_naming.child_or_empty("village_naming");
// If the [village_naming] child is empty, we cannot provide good names.
std::map<map_location,std::string>* village_labels = village_naming.empty() ? nullptr : labels;
for(size_t vx = 0; vx < width; vx += village_x) {
LOG_NG << "village at " << vx << "\n";
for(size_t vy = rng_()%village_y; vy < height; vy += village_y) {
@ -1348,41 +1340,49 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
villages.insert(res);
if ( village_labels != nullptr ) {
name_generator_factory village_name_generator_factory{ village_naming, {"base", "male", "village", "lake", "river", "bridge", "grassland", "forest", "hill", "mountain", "mountain_anonymous", "road", "swamp"} };
village_naming.get_old_attribute("base_names", "male_names", "[village_naming]male_names= is deprecated, use base_names= instead");
//Due to the attribute detection feature of the factory we also support male_name_generator= but keep it undocumented.
base_name_generator = village_name_generator_factory.get_name_generator( (village_naming.has_attribute("base_names") || village_naming.has_attribute("base_name_generator")) ? "base" : "male" );
const map_location loc(res.x-width/3,res.y-height/3);
map_location adj[6];
get_adjacent_tiles(loc,adj);
std::string name_type = "village_name";
std::string name_type = "village";
const t_translation::t_list
field = t_translation::t_list(1, t_translation::GRASS_LAND),
field = t_translation::t_list(1, t_translation::GRASS_LAND),
forest = t_translation::t_list(1, t_translation::FOREST),
mountain = t_translation::t_list(1, t_translation::MOUNTAIN),
hill = t_translation::t_list(1, t_translation::HILL);
hill = t_translation::t_list(1, t_translation::HILL);
size_t field_count = 0, forest_count = 0, mountain_count = 0, hill_count = 0;
utils::string_map symbols;
std::map<std::string,std::string> symbols;
size_t n;
for(n = 0; n != 6; ++n) {
const std::map<location,std::string>::const_iterator road_name = road_names.find(adj[n]);
if(road_name != road_names.end()) {
symbols["road"] = road_name->second;
name_type = "village_name_road";
name_type = "road";
break;
}
const std::map<location,std::string>::const_iterator river_name = river_names.find(adj[n]);
if(river_name != river_names.end()) {
symbols["river"] = river_name->second;
name_type = "village_name_river";
name_type = "river";
const std::map<location,std::string>::const_iterator bridge_name = bridge_names.find(adj[n]);
if(bridge_name != bridge_names.end()) {
//we should always end up here, since if there is an adjacent bridge, there has to be an adjacent river too
symbols["bridge"] = bridge_name->second;
name_type = "village_name_river_bridge";
name_type = "river_bridge";
}
break;
@ -1391,28 +1391,28 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
const std::map<location,std::string>::const_iterator forest_name = forest_names.find(adj[n]);
if(forest_name != forest_names.end()) {
symbols["forest"] = forest_name->second;
name_type = "village_name_forest";
name_type = "forest";
break;
}
const std::map<location,std::string>::const_iterator lake_name = lake_names.find(adj[n]);
if(lake_name != lake_names.end()) {
symbols["lake"] = lake_name->second;
name_type = "village_name_lake";
name_type = "lake";
break;
}
const std::map<location,std::string>::const_iterator mountain_name = mountain_names.find(adj[n]);
if(mountain_name != mountain_names.end()) {
symbols["mountain"] = mountain_name->second;
name_type = "village_name_mountain";
name_type = "mountain";
break;
}
const std::map<location,std::string>::const_iterator swamp_name = swamp_names.find(adj[n]);
if(swamp_name != swamp_names.end()) {
symbols["swamp"] = swamp_name->second;
name_type = "village_name_swamp";
name_type = "swamp";
break;
}
@ -1432,19 +1432,23 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
if(n == 6) {
if(field_count == 6) {
name_type = "village_name_grassland";
name_type = "grassland";
} else if(forest_count >= 2) {
name_type = "village_name_forest";
name_type = "forest";
} else if(mountain_count >= 1) {
name_type = "village_name_mountain_anonymous";
name_type = "name_mountain_anonymous";
} else if(hill_count >= 2) {
name_type = "village_name_hill";
name_type = "hill";
}
}
std::string name;
symbols["base"] = base_name_generator->generate();
std::shared_ptr<name_generator> village_name_generator = village_name_generator_factory.get_name_generator(name_type);
for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
name = generate_name(village_names_generator,name_type,nullptr,&symbols);
name = village_name_generator->generate( symbols );
}
used_names.insert(name);

View file

@ -28,8 +28,8 @@ class unit_race;
#include <boost/random.hpp>
#include <boost/cstdint.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <map>
#include <memory>
class default_map_generator_job
{
@ -44,7 +44,6 @@ public:
bool roads_between_castles, std::map<map_location,std::string>* labels,
const config& cfg);
private:
typedef std::vector<std::vector<int> > height_map;
typedef t_translation::t_map terrain_map;
@ -60,11 +59,9 @@ private:
bool generate_lake(t_translation::t_map& terrain, int x, int y, int lake_fall_off, std::set<map_location>& locs_touched);
map_location random_point_at_side(size_t width, size_t height);
std::string generate_name(boost::shared_ptr<name_generator>& name_generator, const std::string& id,
std::string* base_name=nullptr,
utils::string_map* additional_symbols=nullptr);
boost::random::mt19937 rng_;
const config& game_config_;
};
#endif

View file

@ -104,6 +104,7 @@
#include "units/ptr.hpp" // for unit_const_ptr, unit_ptr
#include "units/types.hpp" // for unit_type_data, unit_types, etc
#include "util.hpp" // for lexical_cast
#include "utils/name_generator.hpp"
#include "utils/markov_generator.hpp"
#include "utils/context_free_grammar_generator.hpp"
#include "variable.hpp" // for vconfig, etc
@ -4374,62 +4375,61 @@ static int intf_name_generator(lua_State *L)
{
std::string type = luaL_checkstring(L, 1);
name_generator* gen = nullptr;
if(type == "markov" || type == "markov_chain") {
std::vector<std::string> input;
if(lua_istable(L, 2)) {
input = lua_check<std::vector<std::string>>(L, 2);
} else {
input = utils::parenthetical_split(luaL_checkstring(L, 2));
}
int chain_sz = luaL_optinteger(L, 3, 2);
int max_len = luaL_optinteger(L, 4, 12);
gen = new(lua_newuserdata(L, sizeof(markov_generator)))
markov_generator(input, chain_sz, max_len);
// Ensure the pointer didn't change when cast
assert(static_cast<void*>(gen) == dynamic_cast<markov_generator*>(gen));
} else if(type == "context_free" || type == "cfg" || type == "CFG") {
void* buf = lua_newuserdata(L, sizeof(context_free_grammar_generator));
if(lua_istable(L, 2)) {
std::map<std::string, std::vector<std::string>> data;
for(lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
if(!lua_isstring(L, -2)) {
lua_pushstring(L, "CFG generator: invalid nonterminal name (must be a string)");
return lua_error(L);
}
if(lua_isstring(L, -1)) {
data[lua_tostring(L,-2)] = utils::split(lua_tostring(L,-1),'|');
} else if(lua_istable(L, -1)) {
data[lua_tostring(L,-2)] = lua_check<std::vector<std::string>>(L, -1);
} else {
lua_pushstring(L, "CFG generator: invalid noterminal value (must be a string or list of strings)");
return lua_error(L);
}
try {
if(type == "markov" || type == "markov_chain") {
std::vector<std::string> input;
if(lua_istable(L, 2)) {
input = lua_check<std::vector<std::string>>(L, 2);
} else {
input = utils::parenthetical_split(luaL_checkstring(L, 2));
}
if(!data.empty()) {
gen = new(buf) context_free_grammar_generator(data);
int chain_sz = luaL_optinteger(L, 3, 2);
int max_len = luaL_optinteger(L, 4, 12);
gen = new(lua_newuserdata(L, sizeof(markov_generator)))
markov_generator(input, chain_sz, max_len);
// Ensure the pointer didn't change when cast
assert(static_cast<void*>(gen) == dynamic_cast<markov_generator*>(gen));
} else if(type == "context_free" || type == "cfg" || type == "CFG") {
void* buf = lua_newuserdata(L, sizeof(context_free_grammar_generator));
if(lua_istable(L, 2)) {
std::map<std::string, std::vector<std::string>> data;
for(lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
if(!lua_isstring(L, -2)) {
lua_pushstring(L, "CFG generator: invalid nonterminal name (must be a string)");
return lua_error(L);
}
if(lua_isstring(L, -1)) {
data[lua_tostring(L,-2)] = utils::split(lua_tostring(L,-1),'|');
} else if(lua_istable(L, -1)) {
data[lua_tostring(L,-2)] = lua_check<std::vector<std::string>>(L, -1);
} else {
lua_pushstring(L, "CFG generator: invalid noterminal value (must be a string or list of strings)");
return lua_error(L);
}
}
if(!data.empty()) {
gen = new(buf) context_free_grammar_generator(data);
}
} else {
gen = new(buf) context_free_grammar_generator(luaL_checkstring(L, 2));
}
if(gen) {
assert(static_cast<void*>(gen) == dynamic_cast<context_free_grammar_generator*>(gen));
}
} else {
gen = new(buf) context_free_grammar_generator(luaL_checkstring(L, 2));
return luaL_argerror(L, 1, "should be either 'markov_chain' or 'context_free'");
}
if(gen) {
assert(static_cast<void*>(gen) == dynamic_cast<context_free_grammar_generator*>(gen));
}
} else {
return luaL_argerror(L, 1, "should be either 'markov_chain' or 'context_free'");
}
static const char*const generic_err = "error initializing name generator";
if(!gen) {
lua_pushstring(L, generic_err);
return lua_error(L);
catch (const name_generator_invalid_exception& ex) {
lua_pushstring(L, ex.what());
return lua_error(L);
}
// We set the metatable now, even if the generator is invalid, so that it
// will be properly collected if it was invalid.
luaL_getmetatable(L, "name generator");
lua_setmetatable(L, -2);
if(!gen->is_valid()) {
lua_pushstring(L, generic_err);
return lua_error(L);
}
return 1;
}

View file

@ -24,8 +24,8 @@
#include "log.hpp"
#include "serialization/string_utils.hpp"
#include "serialization/unicode_cast.hpp"
#include "utils/markov_generator.hpp"
#include "utils/context_free_grammar_generator.hpp"
#include "utils/name_generator.hpp"
#include "utils/name_generator_factory.hpp"
/// Dummy race used when a race is not yet known.
const unit_race unit_race::null_race;
@ -79,6 +79,7 @@ unit_race::unit_race(const config& cfg) :
lg::wml_error() << "[race] '" << cfg["name"] << "' is missing a plural_name field.";
plural_name_ = (cfg["name"]);
}
// use "name" if "male_name" or "female_name" aren't available
name_[MALE] = cfg["male_name"];
if(name_[MALE].empty()) {
@ -89,34 +90,11 @@ unit_race::unit_race(const config& cfg) :
name_[FEMALE] = (cfg["name"]);
}
config::attribute_value male_generator = cfg["male_name_generator"];
config::attribute_value female_generator = cfg["female_name_generator"];
if(male_generator.blank()) {
male_generator = cfg["name_generator"];
}
if(female_generator.blank()) {
female_generator = cfg["name_generator"];
}
name_generator_factory generator_factory = name_generator_factory(cfg, {"male", "female"});
if(!male_generator.blank()) {
name_generator_[MALE].reset(new context_free_grammar_generator(male_generator));
if(!name_generator_[MALE]->is_valid()) {
name_generator_[MALE].reset();
}
}
if(!female_generator.blank()) {
name_generator_[FEMALE].reset(new context_free_grammar_generator(female_generator));
if(!name_generator_[FEMALE]->is_valid()) {
name_generator_[FEMALE].reset();
}
}
int chain_size = cfg["markov_chain_size"].to_int(2);
if(!name_generator_[MALE]) {
name_generator_[MALE].reset(new markov_generator(utils::split(cfg["male_names"]), chain_size, 12));
}
if(!name_generator_[FEMALE]) {
name_generator_[FEMALE].reset(new markov_generator(utils::split(cfg["female_names"]), chain_size, 12));
for(int i=MALE; i<NUM_GENDERS; i++) {
GENDER gender = static_cast<GENDER>(i);
name_generator_[i] = generator_factory.get_name_generator(gender_string(gender));
}
}

View file

@ -17,7 +17,7 @@
#include "config.hpp"
#include "utils/name_generator.hpp"
#include <boost/smart_ptr/shared_ptr.hpp>
#include <memory>
class unit_race
{
@ -59,7 +59,7 @@ private:
t_string plural_name_;
t_string description_;
unsigned int ntraits_;
boost::shared_ptr<name_generator> name_generator_[NUM_GENDERS];
std::shared_ptr<name_generator> name_generator_[NUM_GENDERS];
config::const_child_itors traits_;
config::const_child_itors topics_;

View file

@ -30,8 +30,7 @@ context_free_grammar_generator::~context_free_grammar_generator()
{
}
context_free_grammar_generator::context_free_grammar_generator(const std::string& source) :
initialized_(false)
context_free_grammar_generator::context_free_grammar_generator(const std::string& source)
{
const char* reading = source.c_str();
nonterminal* current = nullptr;
@ -41,7 +40,11 @@ context_free_grammar_generator::context_free_grammar_generator(const std::string
while (*reading != 0) {
if (*reading == '=') {
// Leading and trailing whitespace is not significant, but internal whitespace is
current = &nonterminals_[utils::strip(buf)];
std::string key = utils::strip(buf);
if(key == "!" || key =="(" || key == ")") {
throw name_generator_invalid_exception("[context_free_grammar_generator] Parsing error: nonterminals (, ! and ) may not be overridden");
}
current = &nonterminals_[key];
current->possibilities_.push_back(std::vector<std::string>());
filled = &current->possibilities_.back();
buf.clear();
@ -53,8 +56,7 @@ context_free_grammar_generator::context_free_grammar_generator(const std::string
buf.clear();
} else if (*reading == '|') {
if (!filled || !current) {
lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced | symbol";
return;
throw name_generator_invalid_exception("[context_free_grammar_generator] Parsing error: misplaced | symbol");
}
filled->push_back(buf);
current->possibilities_.push_back(std::vector<std::string>());
@ -69,16 +71,14 @@ context_free_grammar_generator::context_free_grammar_generator(const std::string
} else {
if (*reading == '{') {
if (!filled) {
lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced { symbol";
return;
throw name_generator_invalid_exception("[context_free_grammar_generator] Parsing error: misplaced { symbol");
}
filled->push_back(buf);
buf.clear();
}
else if (*reading == '}') {
if (!filled) {
lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced } symbol";
return;
throw name_generator_invalid_exception("[context_free_grammar_generator] Parsing error: misplaced } symbol");
}
filled->push_back('{' + utils::strip(buf));
buf.clear();
@ -87,16 +87,16 @@ context_free_grammar_generator::context_free_grammar_generator(const std::string
reading++;
}
if (filled) filled->push_back(buf);
initialized_ = true;
}
context_free_grammar_generator::context_free_grammar_generator(const std::map<std::string, std::vector<std::string>>& source) :
initialized_(false)
context_free_grammar_generator::context_free_grammar_generator(const std::map<std::string, std::vector<std::string>>& source)
{
for(auto rule : source) {
std::string key = rule.first; // Need to do this because utils::strip is mutating
key = utils::strip(key);
if(key == "!" || key =="(" || key == ")") {
throw name_generator_invalid_exception("[context_free_grammar_generator] Parsing error: nonterminals (, ! and ) may not be overridden");
}
std::string buf;
for(std::string str : rule.second) {
nonterminals_[key].possibilities_.emplace_back();
@ -105,17 +105,16 @@ context_free_grammar_generator::context_free_grammar_generator(const std::map<st
for(char c : str) {
if (c == '{') {
if (!filled) {
lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced { symbol";
return;
throw name_generator_invalid_exception("[context_free_grammar_generator] Parsing error: misplaced { symbol");
}
filled->push_back(buf);
buf.clear();
}
else if (c == '}') {
if (!filled) {
lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced } symbol";
return;
throw name_generator_invalid_exception("[context_free_grammar_generator] Parsing error: misplaced } symbol");
}
filled->push_back('{' + utils::strip(buf));
buf.clear();
} else buf.push_back(c);
@ -125,36 +124,51 @@ context_free_grammar_generator::context_free_grammar_generator(const std::map<st
}
}
}
initialized_ = true;
}
std::string context_free_grammar_generator::print_nonterminal(const std::string& name, uint32_t* seed, short seed_pos) const {
std::string result;
std::map<std::string, nonterminal>::const_iterator found = nonterminals_.find(name);
if (found == nonterminals_.end()) {
lg::wml_error() << "[context_free_grammar_generator] Warning: needed nonterminal " << name << " not defined";
return "!" + name;
if (name == "!") {
return "|";
}
const context_free_grammar_generator::nonterminal& got = found->second;
unsigned int picked = seed[seed_pos++] % got.possibilities_.size();
if (seed_pos >= seed_size) seed_pos = 0;
if (picked == got.last_) {
picked = seed[seed_pos++] % got.possibilities_.size();
else if (name == "(" ) {
return "{";
}
else if (name == ")" ) {
return "}";
}
else {
std::string result = "";
std::map<std::string,nonterminal>::const_iterator found = nonterminals_.find(name);
if (found == nonterminals_.end()) {
lg::wml_error() << "[context_free_grammar_generator] Warning: needed nonterminal" << name << " not defined";
return "!" + name;
}
const context_free_grammar_generator::nonterminal& got = found->second;
unsigned int picked = seed[seed_pos++] % got.possibilities_.size();
if (seed_pos >= seed_size) seed_pos = 0;
if (picked == got.last_) {
picked = seed[seed_pos++] % got.possibilities_.size();
if (seed_pos >= seed_size) seed_pos = 0;
}
got.last_ = picked;
const std::vector<std::string>& used = got.possibilities_[picked];
for (unsigned int i = 0; i < used.size(); i++) {
if (used[i][0] == '{') result += print_nonterminal(used[i].substr(1), seed, seed_pos);
else result += used[i];
}
return result;
}
got.last_ = picked;
const std::vector<std::string>& used = got.possibilities_[picked];
for (unsigned int i = 0; i < used.size(); i++) {
if (used[i][0] == '{') result += print_nonterminal(used[i].substr(1), seed, seed_pos);
else result += used[i];
}
return result;
}
std::string context_free_grammar_generator::generate() const {
uint32_t seed[seed_size];
init_seed(seed);
return print_nonterminal("main", seed, 0);
}
void context_free_grammar_generator::init_seed(uint32_t seed[]) const {
for (unsigned short int i = 0; i < seed_size; i++) {
seed[i] = random_new::generator->next_random();
}
return print_nonterminal("main", seed, 0);
}

View file

@ -17,8 +17,6 @@
#include "utils/name_generator.hpp"
#include <string>
#include <map>
#include <list>
#include <vector>
@ -32,9 +30,9 @@ private:
mutable unsigned int last_;
};
void init_seed(uint32_t seed[]) const;
std::map<std::string, nonterminal> nonterminals_;
bool initialized_;
std::string print_nonterminal(const std::string& name, uint32_t* seed, short int seed_pos) const;
std::string print_nonterminal(const std::string& name, uint32_t seed[], short int seed_pos) const;
static const short unsigned int seed_size = 20;
public:
@ -54,11 +52,6 @@ public:
std::string generate() const override;
~context_free_grammar_generator();
/** Checks if the object is initialized
* @return if it is initialized
*/
bool is_valid() const override {return initialized_; }
};
#endif

View file

@ -15,21 +15,36 @@
#ifndef NAME_GENERATOR_HPP_INCLUDED
#define NAME_GENERATOR_HPP_INCLUDED
#include "global.hpp"
#include <string>
#include "formula/string_utils.hpp"
#include <exception>
typedef std::map< std::string, t_string > string_map;
class name_generator_invalid_exception : public std::exception {
public:
name_generator_invalid_exception(const char* errMessage):errMessage_(errMessage){}
const char* what() const throw() { return errMessage_; }
private:
const char* errMessage_;
};
class name_generator {
public:
virtual std::string generate() const = 0;
virtual bool is_valid() const {return true;}
virtual ~name_generator() {}
std::string generate(const std::map<std::string,std::string>& variables) const { return utils::interpolate_variables_into_string(generate(), &variables); };
virtual std::string generate() const { return ""; };
name_generator() {};
virtual ~name_generator() {};
};
class proxy_name_generator : public name_generator {
const name_generator& base;
public:
proxy_name_generator(const name_generator& b) : base(b) {}
std::string generate() const override {return base.generate();}
bool is_valid() const override {return base.is_valid();}
std::string generate() const override { return base.generate(); };
};
#endif

View file

@ -0,0 +1,68 @@
/*
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "global.hpp"
#include "log.hpp"
#include "serialization/string_utils.hpp"
#include "utils/name_generator_factory.hpp"
#include "utils/name_generator.hpp"
#include "utils/context_free_grammar_generator.hpp"
#include "utils/markov_generator.hpp"
name_generator_factory::name_generator_factory(const config& config, std::vector<std::string> ids) : name_generators_() {
add_name_generator_from_config(config, "", "");
for (std::vector<std::string>::iterator it = std::begin(ids); it!=std::end(ids); it++) {
std::string id = *it;
add_name_generator_from_config(config, id, (id + "_"));
}
};
void name_generator_factory::add_name_generator_from_config(const config& config, const std::string id, const std::string prefix) {
std::string cfg_name = prefix + "name_generator";
std::string markov_name = prefix + "names";
if(config.has_attribute(cfg_name)) {
try {
name_generators_[id] = std::shared_ptr<name_generator>(new context_free_grammar_generator(config[cfg_name]));
}
catch (const name_generator_invalid_exception& ex) { lg::wml_error() << ex.what() << '\n'; }
}
if(config.has_attribute(markov_name)) {
config::attribute_value markov_name_list = config[markov_name];
if(!markov_name_list.blank()) {
name_generators_[id] = std::shared_ptr<name_generator>(new markov_generator(utils::split(markov_name_list), config["markov_chain_size"].to_int(2), 12));
}
}
};
std::shared_ptr<name_generator> name_generator_factory::get_name_generator() {
std::map<std::string, std::shared_ptr<name_generator>>::const_iterator it = name_generators_.find("");
if(it == name_generators_.end()) {
//create a dummy instance, which always returns the empty string
return std::shared_ptr<name_generator>(new name_generator( ));
}
return it->second;
};
std::shared_ptr<name_generator> name_generator_factory::get_name_generator(const std::string id) {
std::map<std::string, std::shared_ptr<name_generator>>::const_iterator it = name_generators_.find(id);
if(it == name_generators_.end()) {
return get_name_generator();
}
return it->second;
};

View file

@ -0,0 +1,59 @@
/*
Copyright (C) 2016 by Marius Spix
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#ifndef NAME_GENERATOR_FACTORY_HPP_INCLUDED
#define NAME_GENERATOR_FACTORY_HPP_INCLUDED
#include "utils/name_generator.hpp"
#include "config.hpp"
#include <vector>
#include <memory>
class name_generator_factory
{
public:
/**
* Creates a new name generator factory
* @param config the WML data to be parsed for name generators
* @param ids a list of generator ids, e.g. genders or terrain types
*/
name_generator_factory(const config& config, std::vector<std::string> ids);
/**
* Gets the default name generator
* @returns the default name generator
*/
std::shared_ptr<name_generator> get_name_generator();
/**
* Gets a specific name generator or the default name generator, if the
* specific name generator is not found.
* @param name generator id, e.g. a gender or a terrain type
* @returns a name generator
*/
std::shared_ptr<name_generator> get_name_generator(const std::string name);
private:
std::map<std::string, std::shared_ptr<name_generator>> name_generators_;
/**
* Determines a name generator from WML data
* @param config the WML data to be parsed for name generators
* @param the prefix to look for
* @returns a name generator or nullptr if not found
*/
void add_name_generator_from_config(const config& config, const std::string id, const std::string prefix);
};
#endif