The Battle for Wesnoth  1.15.0-dev
default_map_generator.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
21 #include "gettext.hpp"
22 #include "log.hpp"
23 #include "map/map.hpp"
24 #include "seed_rng.hpp"
25 
26 static lg::log_domain log_engine("engine");
27 #define DBG_NG LOG_STREAM(debug, log_engine)
28 
29 namespace {
30  const int max_island = 10;
31  const int max_coastal = 5;
32 }
33 
35  : width(std::max(0, cfg["map_width"].to_int(40)))
36  , height(std::max(0, cfg["map_height"].to_int(40)))
37  , default_width(width)
38  , default_height(height)
39  , nplayers(std::max(0, cfg["players"].to_int(2)))
40  , nvillages(std::max(0, cfg["villages"].to_int(25)))
41  , iterations(std::max(0, cfg["iterations"].to_int(1000)))
42  , hill_size(std::max(0, cfg["hill_size"].to_int(10)))
43  , castle_size(std::max(0, cfg["castle_size"].to_int(9)))
44  , island_size(std::max(0, cfg["island_size"].to_int(0)))
45  , island_off_center(0)
46  , max_lakes(std::max(0, cfg["max_lakes"].to_int(20)))
47  , link_castles(true)
48  , show_labels(true)
49 {
50 }
51 
53  : cfg_(cfg)
54  , data_(cfg)
55 {
56 }
57 
58 bool default_map_generator::allow_user_config() const { return true; }
59 
61 {
62  gui2::dialogs::generator_settings::execute(data_);
63 }
64 
65 std::string default_map_generator::name() const { return "default"; }
66 
68 {
69  if (const config &c = cfg_.child("scenario"))
70  return c["name"];
71 
72  return std::string();
73 }
74 
75 std::string default_map_generator::create_map(boost::optional<uint32_t> randomseed)
76 {
77  return generate_map(nullptr, randomseed);
78 }
79 
80 std::string default_map_generator::generate_map(std::map<map_location,std::string>* labels, boost::optional<uint32_t> randomseed)
81 {
82  uint32_t seed;
83  if(const uint32_t* pseed = randomseed.get_ptr()) {
84  seed = *pseed;
85  } else {
86  seed = seed_rng::next_seed();
87  }
88 
89  /* We construct a copy of the generator data and modify it as needed. This ensures every time
90  * this function is called the generator job gets a fresh set of settings, and that the internal
91  * copy of the settings are never touched except by the settings dialog.
92  *
93  * The original data is still used for conditional checks and calculations, but any modifications
94  * should be done on this object.
95  */
96  generator_data job_data = data_;
97 
98  // Suppress labels?
99  if(!data_.show_labels) {
100  labels = nullptr;
101  }
102 
103  // The random generator thinks odd widths are nasty, so make them even
104  if(is_odd(data_.width)) {
105  ++job_data.width;
106  }
107 
109  job_data.island_size = 0;
110  job_data.nvillages = (data_.nvillages * data_.width * data_.height) / 1000;
111  job_data.island_off_center = 0;
112 
113  if(data_.island_size >= max_coastal) {
114  // Islands look good with much fewer iterations than normal, and fewer lakes
115  job_data.iterations /= 10;
116  job_data.max_lakes /= 9;
117 
118  // The radius of the island should be up to half the width of the map
119  const int island_radius = 50 + ((max_island - data_.island_size) * 50)/(max_island - max_coastal);
120  job_data.island_size = (island_radius * (data_.width/2))/100;
121  } else if(data_.island_size > 0) {
122  // The radius of the island should be up to twice the width of the map
123  const int island_radius = 40 + ((max_coastal - data_.island_size) * 40)/max_coastal;
124  job_data.island_size = (island_radius * data_.width * 2)/100;
125  job_data.island_off_center = std::min(data_.width, data_.height);
126  DBG_NG << "calculated coastal params...\n";
127  }
128 
129  // A map generator can fail so try a few times to get a map before aborting.
130  std::string map;
131 
132  // Keep a copy of labels as it can be written to by the map generator func
133  std::map<map_location,std::string> labels_copy;
134  std::map<map_location,std::string>* labels_ptr = labels ? &labels_copy : nullptr;
135 
136  // Iinitilize the job outside the loop so that we really get a different result every time we run the loop.
137  default_map_generator_job job(seed);
138 
139  int tries = 10;
140  std::string error_message;
141  do {
142  // Reset the labels.
143  if(labels) {
144  labels_copy = *labels;
145  }
146 
147  try {
148  map = job.default_generate_map(job_data, labels_ptr, cfg_);
149  error_message = "";
150  } catch(const mapgen_exception& exc) {
151  error_message = exc.message;
152  }
153 
154  --tries;
155  } while(tries && map.empty());
156 
157  if(labels) {
158  labels->swap(labels_copy);
159  }
160 
161  if(!error_message.empty()) {
162  throw mapgen_exception(error_message);
163  }
164 
165  return map;
166 }
167 
168 config default_map_generator::create_scenario(boost::optional<uint32_t> randomseed)
169 {
170  DBG_NG << "creating scenario...\n";
171 
172  config res = cfg_.child_or_empty("scenario");
173 
174  DBG_NG << "got scenario data...\n";
175 
176  std::map<map_location,std::string> labels;
177  DBG_NG << "generating map...\n";
178 
179  try{
180  res["map_data"] = generate_map(&labels, randomseed);
181  }
182  catch (const mapgen_exception& exc){
183  res["map_data"] = "";
184  res["error_message"] = exc.message;
185  }
186  DBG_NG << "done generating map..\n";
187 
188  for(std::map<map_location,std::string>::const_iterator i =
189  labels.begin(); i != labels.end(); ++i) {
190 
191  if(i->first.x >= 0 && i->first.y >= 0 &&
192  i->first.x < static_cast<long>(data_.width) &&
193  i->first.y < static_cast<long>(data_.height)) {
194 
195  config& label = res.add_child("label");
196  label["text"] = i->second;
197  label["category"] = "villages";
198  i->first.write(label);
199  }
200  }
201 
202  return res;
203 }
bool is_odd(T num)
Definition: math.hpp:34
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:424
std::string name() const override
Returns a string identifying the generator by name.
STL namespace.
static lg::log_domain log_engine("engine")
std::string config_name() const override
Return a friendly name for the generator used to differentiate between different configs of the same ...
generator_data()=default
std::string create_map(boost::optional< uint32_t > randomseed) override
Creates a new map and returns it.
config create_scenario(boost::optional< uint32_t > randomseed) override
#define DBG_NG
void user_config() override
Display the interactive screen, which allows the user to modify how the generator behaves...
std::string default_generate_map(generator_data data, std::map< map_location, std::string > *labels, const config &cfg)
Generate the map.
std::size_t i
Definition: function.cpp:933
bool allow_user_config() const override
Returns true if the map generator has an interactive screen, which allows the user to modify how the ...
default_map_generator(const config &game_config)
config & add_child(config_key_type key)
Definition: config.cpp:480
uint32_t next_seed()
Definition: seed_rng.cpp:45
std::string generate_map(std::map< map_location, std::string > *labels, boost::optional< uint32_t > randomseed)
Standard logging facilities (interface).
std::string message
Definition: exceptions.hpp:31
static int max_coastal
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:457
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
mock_char c