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