Merge pull request #661 from spixi/name_and_map_generator

Improvements in the name and map generators
This commit is contained in:
Celtic Minstrel 2016-07-18 01:10:46 -04:00
commit 7286ea8e0f
18 changed files with 469 additions and 319 deletions

View file

@ -196,7 +196,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
* Fix wesnoth.erase_unit failing if the unit was on a recall list.
* WML tables defined in Lua now accept string keys with array values
(where "array" is a table whose keys are all integers). This joins
@ -331,6 +331,9 @@ Version 1.13.4+dev:
passing to tomap() - this also means key-value pairs are now
serializable (relevant in FormulaAI)
* sgn(), trunc() and frac() functions for decimal numbers
* 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_anon_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

@ -109,6 +109,7 @@
9164077D1D3C37D30057C4DE /* location_palette.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 914F2F841D35253900A42440 /* location_palette.cpp */; };
9164077E1D3C37F60057C4DE /* chat_events.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 918C8A181D05F9AA009744A0 /* chat_events.cpp */; };
9164077F1D3C381B0057C4DE /* chat_command_handler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 918C8A161D05F9AA009744A0 /* chat_command_handler.cpp */; };
913D26771D3C9697002FF3AB /* name_generator_factory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 913D26751D3C9697002FF3AB /* name_generator_factory.cpp */; };
916718E61CADA3BF00B055A9 /* connect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5A9BCB00ECA805A002BE442 /* connect.cpp */; };
916718E71CADA3BF00B055A9 /* description.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4944F4161354FBB20027E614 /* description.cpp */; };
916718E81CADA3BF00B055A9 /* filter_options.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 620091C11575C784009AA2C9 /* filter_options.cpp */; };
@ -1554,6 +1555,8 @@
9130A45F1C73BB6100852782 /* select_orb_colors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = select_orb_colors.hpp; sourceTree = "<group>"; };
914F2F841D35253900A42440 /* location_palette.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = location_palette.cpp; sourceTree = "<group>"; };
914F2F851D35253900A42440 /* location_palette.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = location_palette.hpp; sourceTree = "<group>"; };
913D26751D3C9697002FF3AB /* name_generator_factory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = name_generator_factory.cpp; sourceTree = "<group>"; };
913D26761D3C9697002FF3AB /* name_generator_factory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = name_generator_factory.hpp; sourceTree = "<group>"; };
918056BE1CB1E4C0001A7F35 /* functional.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = functional.hpp; sourceTree = "<group>"; };
918C8A161D05F9AA009744A0 /* chat_command_handler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = chat_command_handler.cpp; sourceTree = "<group>"; };
918C8A171D05F9AA009744A0 /* chat_command_handler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = chat_command_handler.hpp; sourceTree = "<group>"; };
@ -3580,6 +3583,8 @@
91FBBAD91CB6D1B700470BFE /* markov_generator.cpp */,
91FBBADA1CB6D1B700470BFE /* markov_generator.hpp */,
91C55DA11CC078780040012E /* name_generator.hpp */,
913D26751D3C9697002FF3AB /* name_generator_factory.cpp */,
913D26761D3C9697002FF3AB /* name_generator_factory.hpp */,
91EF6C001C9E22E400E2A733 /* reference_counter.hpp */,
91EF6C011C9E22E400E2A733 /* sha1.cpp */,
91EF6C021C9E22E400E2A733 /* sha1.hpp */,
@ -5204,6 +5209,7 @@
918C8A201D05F9AA009744A0 /* wesnothd_connection.cpp in Sources */,
918C8A231D05FDAA009744A0 /* logging.cpp in Sources */,
914F2F861D35253900A42440 /* location_palette.cpp in Sources */,
913D26771D3C9697002FF3AB /* name_generator_factory.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -959,6 +959,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
wesnothd_connection.cpp

View file

@ -562,6 +562,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
wesnothd_connection.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");
@ -41,14 +41,14 @@ static lg::log_domain log_mapgen("mapgen");
default_map_generator_job::default_map_generator_job()
: 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)
, game_config_(game_config_manager::get()->game_config())
{
}
@ -69,9 +69,7 @@ typedef map_location location;
* the center of the map will be inverted (i.e. be valleys). 'island_size' as
* 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 island_size, size_t island_off_center)
height_map default_map_generator_job::generate_height_map(size_t width, size_t height, 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));
@ -89,16 +87,18 @@ height_map default_map_generator_job::generate_height_map(size_t width, size_t h
center_y += island_off_center;
break;
case 2:
if(center_x < island_off_center)
if(center_x < island_off_center) {
center_x = 0;
else
} else {
center_x -= island_off_center;
}
break;
case 3:
if(center_y < island_off_center)
if(center_y < island_off_center) {
center_y = 0;
else
} else {
center_y -= island_off_center;
}
break;
}
}
@ -118,10 +118,8 @@ height_map default_map_generator_job::generate_height_map(size_t width, size_t h
// Is this a negative hill? (i.e. a valley)
bool is_valley = false;
int x1 = island_size > 0 ? center_x - island_size + (rng_()%(island_size*2)) :
int(rng_()%width);
int y1 = island_size > 0 ? center_y - island_size + (rng_()%(island_size*2)) :
int(rng_()%height);
int x1 = island_size > 0 ? center_x - island_size + (rng_()%(island_size*2)) : int(rng_()%width);
int y1 = island_size > 0 ? center_y - island_size + (rng_()%(island_size*2)) : int(rng_()%height);
// We have to check whether this is actually a valley
if(island_size != 0) {
@ -164,11 +162,13 @@ height_map default_map_generator_job::generate_height_map(size_t width, size_t h
int heighest = 0, lowest = 100000, x;
for(x = 0; size_t(x) != res.size(); ++x) {
for(int y = 0; size_t(y) != res[x].size(); ++y) {
if(res[x][y] > heighest)
if(res[x][y] > heighest) {
heighest = res[x][y];
}
if(res[x][y] < lowest)
if(res[x][y] < lowest) {
lowest = res[x][y];
}
}
}
@ -178,8 +178,9 @@ height_map default_map_generator_job::generate_height_map(size_t width, size_t h
for(int y = 0; size_t(y) != res[x].size(); ++y) {
res[x][y] -= lowest;
res[x][y] *= 1000;
if(heighest != 0)
if(heighest != 0) {
res[x][y] /= heighest;
}
}
}
@ -273,7 +274,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;
}
@ -379,7 +380,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.
@ -404,7 +405,7 @@ double road_path_calculator::cost(const location& loc,
const double /*so_far*/) const
{
++calls;
if (loc.x < 0 || loc.y < 0 || loc.x >= static_cast<long>(map_.size()) ||
if(loc.x < 0 || loc.y < 0 || loc.x >= static_cast<long>(map_.size()) ||
loc.y >= static_cast<long>(map_.front().size())) {
return (pathfind::cost_calculator::getNoPathValue());
@ -419,7 +420,7 @@ double road_path_calculator::cost(const location& loc,
double windiness = 1.0;
if (windiness_ > 1) {
if(windiness_ > 1) {
// modified pseudo_random taken from builder.cpp
unsigned int a = (loc.x + 92872973) ^ 918273;
unsigned int b = (loc.y + 1672517) ^ 128123;
@ -441,7 +442,7 @@ double road_path_calculator::cost(const location& loc,
static std::string terrain;
terrain = t_translation::write_terrain_code(c);
double res = getNoPathValue();
if (const config &child = cfg_.find_child("road_cost", "terrain", terrain)) {
if(const config &child = cfg_.find_child("road_cost", "terrain", terrain)) {
res = child["cost"].to_double();
}
@ -477,8 +478,7 @@ bool is_valid_terrain::operator()(int x, int y) const
}
static int rank_castle_location(int x, int y, const is_valid_terrain& valid_terrain, int min_x, int max_x, int min_y, int max_y,
size_t min_distance, const std::vector<map_location>& other_castles, int highest_ranking)
static int rank_castle_location(int x, int y, const is_valid_terrain& valid_terrain, int min_x, int max_x, int min_y, int max_y, size_t min_distance, const std::vector<map_location>& other_castles, int highest_ranking)
{
const map_location loc(x,y);
@ -517,8 +517,7 @@ static int rank_castle_location(int x, int y, const is_valid_terrain& valid_terr
const int x_from_border = std::min<int>(x - min_x,max_x - x);
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;
const int border_ranking = min_distance - std::min<int>(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;
@ -564,10 +563,10 @@ static map_location place_village(const t_translation::t_map& map,
const t_translation::t_terrain t = map[i->x][i->y];
const std::string str = t_translation::write_terrain_code(t);
if (const config &child = cfg.find_child("village", "terrain", str)) {
if(const config &child = cfg.find_child("village", "terrain", str)) {
tcode_list_cache::iterator l = adj_liked_cache.find(t);
t_translation::t_list *adjacent_liked;
if (l != adj_liked_cache.end()) {
if(l != adj_liked_cache.end()) {
adjacent_liked = &(l->second);
} else {
adj_liked_cache[t] = t_translation::read_list(child["adjacent_liked"]);
@ -599,35 +598,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() && name_generator != nullptr) {
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,
@ -637,10 +607,10 @@ static void flood_name(const map_location& start, const std::string& name, std::
get_adjacent_tiles(start,adj);
size_t n;
//if adjacent tiles are tiles and unnamed, name them
for (n = 0; n < 6; n++) {
for(n = 0; n < 6; n++) {
//we do not care for tiles outside the middle part
//cast to unsigned to skip x < 0 || y < 0 as well.
if (unsigned(adj[n].x) >= width / 3 || unsigned(adj[n].y) >= height / 3) {
if(unsigned(adj[n].x) >= width / 3 || unsigned(adj[n].y) >= height / 3) {
continue;
}
@ -649,7 +619,7 @@ static void flood_name(const map_location& start, const std::string& name, std::
if((t_translation::terrain_matches(terr, tile_types)) && (tile_names.find(loc) == tile_names.end())) {
tile_names.insert(std::pair<location, std::string>(loc, name));
//labeling decision: this is result of trial and error on what looks best in game
if (label_count % 6 == 0) { //ensure that labels do not occur more often than every 6 recursions
if(label_count % 6 == 0) { //ensure that labels do not occur more often than every 6 recursions
labels->insert(std::pair<map_location, std::string>(loc, full_name));
label_count++; //ensure that no adjacent tiles get labeled
}
@ -732,8 +702,7 @@ terrain_converter::terrain_converter(const config& cfg)
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;
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;
}
t_translation::t_terrain terrain_converter::convert_to() const
@ -743,9 +712,7 @@ 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,
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,
std::map<map_location,std::string>* labels, const config& cfg)
{
log_scope("map generation");
@ -753,6 +720,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.
@ -778,21 +752,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" );
} else {
base_name_generator = base_generator_factory.get_name_generator("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));
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;
@ -805,8 +803,7 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
size_t x, y;
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) {
for(std::vector<terrain_height_mapper>::const_iterator i = height_conversion.begin(); i != height_conversion.end(); ++i) {
if(i->convert_terrain(heights[x][y])) {
terrain[x][y] = i->convert_to();
break;
@ -842,14 +839,14 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
for(int tries = 0; tries != 100; ++tries) {
const int x = rng_()%width;
const int y = rng_()%height;
if (heights[x][y] > cfg["min_lake_height"].to_int()) {
if(heights[x][y] > cfg["min_lake_height"].to_int()) {
std::vector<location> river = generate_river(heights,
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) {
@ -872,8 +869,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;
@ -965,13 +962,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.
@ -1047,7 +1037,7 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
std::set<location> bridges;
road_path_calculator calc(terrain, cfg, rng_());
for (int road = 0; road != nroads; ++road) {
for(int road = 0; road != nroads; ++road) {
log_scope("creating road");
/*
@ -1063,7 +1053,7 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
dst.x += width/3 - 1;
dst.y += height/3 - 1;
if (roads_between_castles && road < int(castles.size() * castles.size())) {
if(roads_between_castles && road < int(castles.size() * castles.size())) {
const size_t src_castle = road/castles.size();
const size_t dst_castle = road%castles.size();
if(src_castle >= dst_castle) {
@ -1079,15 +1069,15 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
continue;
}
if (calc.cost(src, 0.0) >= 1000.0 || calc.cost(dst, 0.0) >= 1000.0) {
if(calc.cost(src, 0.0) >= 1000.0 || calc.cost(dst, 0.0) >= 1000.0) {
continue;
}
// 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;
@ -1109,8 +1099,7 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
// Find the configuration which tells us
// what to convert this tile to, to make it into a road.
if (const config &child = cfg.find_child("road_cost", "terrain",
t_translation::write_terrain_code(terrain[x][y])))
if(const config &child = cfg.find_child("road_cost", "terrain", t_translation::write_terrain_code(terrain[x][y])))
{
// Convert to bridge means that we want to convert
// depending upon the direction the road is going.
@ -1151,8 +1140,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
@ -1183,7 +1172,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));
}
}
}
@ -1206,22 +1195,20 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
terrain[x][y] = t_translation::HUMAN_KEEP;
const int castles[13][2] = {
{-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {0, 1}, {-1, 1},
{-2, 1}, {-2, 0}, {-2, -1}, {-1, -2}, {0, -2}, {1, -2}
{-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {0, 1}, {-1, 1},
{-2, 1}, {-2, 0}, {-2, -1}, {-1, -2}, {0, -2}, {1, -2}
};
for (size_t i = 0; i < castle_size - 1; i++) {
terrain[x+castles[i][0]][y+castles[i][1]] = t_translation::HUMAN_CASTLE;
for(size_t i = 0; i < castle_size - 1; i++) {
terrain[x+castles[i][0]][y+castles[i][1]] = t_translation::HUMAN_CASTLE;
}
// Remove all labels under the castle tiles
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,
y+castles[i][1]-height/3));
}
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, y+castles[i][1]-height/3));
}
}
}
@ -1233,72 +1220,57 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
*we name these now that everything else is placed (as e.g., placing
* roads could split a forest)
*/
if ( misc_labels != nullptr ) {
for (x = width / 3; x < (width / 3)*2; x++) {
for (y = height / 3; y < (height / 3) * 2;y++) {
if(misc_labels != nullptr) {
for(x = width / 3; x < (width / 3)*2; x++) {
for(y = height / 3; y < (height / 3) * 2;y++) {
//check the terrain of the tile
const location loc(x - width / 3, y - height / 3);
const t_translation::t_terrain terr = terrain[x][y];
std::string name, base_name;
std::set<std::string> used_names;
if (t_translation::terrain_matches(terr, t_translation::ALL_MOUNTAINS)) {
if(t_translation::terrain_matches(terr, t_translation::ALL_MOUNTAINS)) {
//name every 15th mountain
if ((rng_()%15) == 0) {
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));
}
}
else if (t_translation::terrain_matches(terr, t_translation::ALL_FORESTS)) {
else if(t_translation::terrain_matches(terr, t_translation::ALL_FORESTS)) {
//if the forest tile is not named yet, name it
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
flood_name(loc, base_name, forest_names, t_translation::ALL_FORESTS, terrain, width, height, 0, misc_labels, name);
}
}
else if (t_translation::terrain_matches(terr, t_translation::ALL_SWAMPS)) {
else if(t_translation::terrain_matches(terr, t_translation::ALL_SWAMPS)) {
//if the swamp tile is not named yet, name it
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
flood_name(loc, base_name, swamp_names, t_translation::ALL_SWAMPS, terrain, width, height, 0, misc_labels, name);
}
}
}//for (y)
}//for (x)
}//if (misc_labels)
}//for(y)
}//for(x)
}//if(misc_labels)
if (nvillages > 0)
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;
@ -1317,6 +1289,17 @@ 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) {
@ -1335,7 +1318,7 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
const std::string str =
t_translation::write_terrain_code(terrain[res.x][res.y]);
if (const config &child = cfg.find_child("village", "terrain", str))
if(const config &child = cfg.find_child("village", "terrain", str))
{
const std::string &convert_to = child["convert_to"];
if(convert_to != "") {
@ -1344,42 +1327,50 @@ std::string default_map_generator_job::default_generate_map(size_t width, size_t
villages.insert(res);
if ( village_labels != nullptr ) {
if(village_labels != nullptr) {
name_generator_factory village_name_generator_factory{ village_naming, {"base", "male", "village", "lake", "river", "bridge", "grassland", "forest", "hill", "mountain", "mountain_anon", "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";
//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 = "river_bridge";
}
break;
@ -1388,28 +1379,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;
}
@ -1429,19 +1420,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 = "mountain_anon";
} 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

@ -41,6 +41,7 @@
#include "serialization/string_utils.hpp"
#include "utils/functional.hpp"
#include "utils/name_generator.hpp"
#include "utils/markov_generator.hpp"
#include "utils/context_free_grammar_generator.hpp"
#include <boost/scoped_ptr.hpp>
@ -186,61 +187,60 @@ 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);
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, Gen);
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,17 @@ 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();
std::vector<std::string>* filled = &nonterminals_[key].possibilities_.back();
@ -105,17 +106,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 +125,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>
#include <cstdint>
@ -33,9 +31,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:
@ -55,11 +53,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,12 +15,27 @@
#ifndef NAME_GENERATOR_HPP_INCLUDED
#define NAME_GENERATOR_HPP_INCLUDED
#include "global.hpp"
#include <string>
#include <map>
#include <exception>
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;}
// Defined in name_generator_factory.cpp
std::string generate(const std::map<std::string,std::string>& variables) const;
virtual std::string generate() const { return ""; }
name_generator() {}
virtual ~name_generator() {}
};
@ -28,8 +43,7 @@ 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,75 @@
/*
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"
#include "formula/string_utils.hpp"
std::string name_generator::generate(const std::map<std::string,std::string>& variables) const {
return utils::interpolate_variables_into_string(generate(), &variables);
}
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