The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
default_map_generator.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
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
27static lg::log_domain log_engine("engine");
28#define DBG_NG LOG_STREAM(debug, log_engine)
29
30namespace {
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
59bool default_map_generator::allow_user_config() const { return true; }
60
62{
63 gui2::dialogs::generator_settings::execute(data_);
64}
65
66std::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
76std::string default_map_generator::create_map(utils::optional<uint32_t> randomseed)
77{
78 return generate_map(nullptr, randomseed);
79}
80
81std::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.
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
169config 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}
#define DBG_NG
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition config.cpp:380
const config & child_or_empty(std::string_view key) const
Returns the first child with the given key, or an empty config if there is none.
Definition config.cpp:390
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")
const config * cfg
static std::string _(const char *str)
Definition gettext.hpp:100
std::string label
What to show in the filter's drop-down list.
Definition manager.cpp:201
Standard logging facilities (interface).
constexpr bool is_odd(T num)
Definition math.hpp:34
uint32_t next_seed()
Definition seed_rng.cpp:32
STL namespace.
std::string message
generator_data()=default
#define i
#define c