The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cave_map_generator.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2017 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project http://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 /**
16  * @file
17  * Map-generator for caves.
18  */
19 
21 #include "log.hpp"
22 #include "map/map.hpp"
23 #include "pathfind/pathfind.hpp"
25 #include "seed_rng.hpp"
26 
27 static lg::log_domain log_engine("engine");
28 #define LOG_NG LOG_STREAM(info, log_engine)
29 
31  wall_(t_translation::CAVE_WALL),
32  clear_(t_translation::CAVE),
34  castle_(t_translation::DWARVEN_CASTLE),
36  cfg_(cfg ? cfg : config()),
37  width_(50),
38  height_(50),
39  village_density_(0),
40  flipx_chance_(cfg_["flipx_chance"]),
41  flipy_chance_(cfg_["flipy_chance"])
42 {
43  width_ = cfg_["map_width"];
44  height_ = cfg_["map_height"];
45 
46  village_density_ = cfg_["village_density"];
47 }
48 
50 {
51  return "";
52 }
53 
55 {
56  if(flipx_) {
57  x = params.width_ - x - 1;
58  }
59 
60  return x;
61 }
62 
64 {
65  if(flipy_) {
66  y = params.height_ - y - 1;
67  }
68 
69  return y;
70 }
71 
72 std::string cave_map_generator::create_map(boost::optional<uint32_t> randomseed)
73 {
74  const config res = create_scenario(randomseed);
75  return res["map_data"];
76 }
77 
78 config cave_map_generator::create_scenario(boost::optional<uint32_t> randomseed)
79 {
80  cave_map_generator_job job(*this, randomseed);
81  return job.res_;
82 }
83 
85  : params(pparams)
86  , flipx_(false)
87  , flipy_(false)
88  , map_(t_translation::ter_map(params.width_ + 2 * gamemap::default_border, params.height_ + 2 * gamemap::default_border/*, params.wall_*/))
89  , starting_positions_()
90  , chamber_ids_()
91  , chambers_()
92  , passages_()
93  , res_(params.cfg_.child_or_empty("settings"))
94  , rng_() //initialises with rand()
95 {
96  res_.add_child("event", config {
97  "name", "start",
98  "message", config {
99  "message", "scenario_generation=cave is deprecated and will be removed soon.",
100  },
101  });
102  uint32_t seed = randomseed.get_ptr() ? *randomseed.get_ptr() : seed_rng::next_seed();
103  rng_.seed(seed);
104  LOG_NG << "creating random cave with seed: " << seed << '\n';
105  flipx_ = int(rng_() % 100) < params.flipx_chance_;
106  flipy_ = int(rng_() % 100) < params.flipy_chance_;
107 
108  LOG_NG << "creating scenario....\n";
110 
111  LOG_NG << "placing chambers...\n";
112  for(std::vector<chamber>::const_iterator c = chambers_.begin(); c != chambers_.end(); ++c) {
113  place_chamber(*c);
114  }
115 
116  LOG_NG << "placing passages...\n";
117 
118  for(std::vector<passage>::const_iterator p = passages_.begin(); p != passages_.end(); ++p) {
119  place_passage(*p);
120  }
121  LOG_NG << "outputting map....\n";
122 
124 }
125 
126 void cave_map_generator::cave_map_generator_job::build_chamber(map_location loc, std::set<map_location>& locs, size_t size, size_t jagged)
127 {
128  if(size == 0 || locs.count(loc) != 0 || !params.on_board(loc))
129  return;
130 
131  locs.insert(loc);
132 
133  map_location adj[6];
134  get_adjacent_tiles(loc,adj);
135  for(size_t n = 0; n != 6; ++n) {
136  if(int(rng_() % 100) < (100l - static_cast<long>(jagged))) {
137  build_chamber(adj[n],locs,size-1,jagged);
138  }
139  }
140 }
141 
143 {
144  for (const config &ch : params.cfg_.child_range("chamber"))
145  {
146  // If there is only a chance of the chamber appearing, deal with that here.
147  if (ch.has_attribute("chance") && int(rng_() % 100) < ch["chance"].to_int()) {
148  continue;
149  }
150 
151  const std::string &xpos = ch["x"];
152  const std::string &ypos = ch["y"];
153 
154  size_t min_xpos = 0, min_ypos = 0, max_xpos = params.width_, max_ypos = params.height_;
155 
156  if (!xpos.empty()) {
157  const std::vector<std::string>& items = utils::split(xpos, '-');
158  if(items.empty() == false) {
159  try {
160  min_xpos = std::stoi(items.front()) - 1;
161  max_xpos = std::stoi(items.back());
162  } catch(std::invalid_argument) {
163  lg::wml_error() << "Invalid min/max coordinates in cave_map_generator: " << items.front() << ", " << items.back() << "\n";
164  continue;
165  }
166  }
167  }
168 
169  if (!ypos.empty()) {
170  const std::vector<std::string>& items = utils::split(ypos, '-');
171  if(items.empty() == false) {
172  try {
173  min_ypos = std::stoi(items.front()) - 1;
174  max_ypos = std::stoi(items.back());
175  } catch(std::invalid_argument) {
176  lg::wml_error() << "Invalid min/max coordinates in cave_map_generator: " << items.front() << ", " << items.back() << "\n";
177  }
178  }
179  }
180  const size_t x = translate_x(min_xpos + (rng_()%(max_xpos-min_xpos)));
181  const size_t y = translate_y(min_ypos + (rng_()%(max_ypos-min_ypos)));
182 
183  int chamber_size = ch["size"].to_int(3);
184  int jagged_edges = ch["jagged"];
185 
186  chamber new_chamber;
187  new_chamber.center = map_location(x,y);
188  build_chamber(new_chamber.center,new_chamber.locs,chamber_size,jagged_edges);
189 
190  const config &items = ch.child("items");
191  new_chamber.items = items ? &items : nullptr;
192 
193  const std::string &id = ch["id"];
194  if (!id.empty()) {
195  chamber_ids_[id] = chambers_.size();
196  }
197 
198  chambers_.push_back(new_chamber);
199 
200  for(const config &p : ch.child_range("passage"))
201  {
202  const std::string &dst = p["destination"];
203 
204  // Find the destination of this passage
205  const std::map<std::string,size_t>::const_iterator itor = chamber_ids_.find(dst);
206  if(itor == chamber_ids_.end())
207  continue;
208 
209  assert(itor->second < chambers_.size());
210 
211  passages_.emplace_back(new_chamber.center, chambers_[itor->second].center, p);
212  }
213  }
214 }
215 
217 {
218  for(std::set<map_location>::const_iterator i = c.locs.begin(); i != c.locs.end(); ++i) {
219  set_terrain(*i,params.clear_);
220  }
221 
222  if (c.items == nullptr || c.locs.empty()) return;
223 
224  size_t index = 0;
225  for (const config::any_child &it : c.items->all_children_range())
226  {
227  config cfg = it.cfg;
228  config &filter = cfg.child("filter");
229  config* object_filter = nullptr;
230  if (config &object = cfg.child("object")) {
231  if (config &of = object.child("filter"))
232  object_filter = &of;
233  }
234 
235  if (!it.cfg["same_location_as_previous"].to_bool()) {
236  index = rng_()%c.locs.size();
237  }
238  std::string loc_var = it.cfg["store_location_as"];
239 
240  std::set<map_location>::const_iterator loc = c.locs.begin();
241  std::advance(loc,index);
242 
243  cfg["x"] = loc->x + 1;
244  cfg["y"] = loc->y + 1;
245 
246  if (filter) {
247  filter["x"] = loc->x + 1;
248  filter["y"] = loc->y + 1;
249  }
250 
251  if (object_filter) {
252  (*object_filter)["x"] = loc->x + 1;
253  (*object_filter)["y"] = loc->y + 1;
254  }
255 
256  // If this is a side, place a castle for the side
257  if (it.key == "side" && !it.cfg["no_castle"].to_bool()) {
258  place_castle(it.cfg["side"].to_int(-1), *loc);
259  }
260 
261  res_.add_child(it.key, cfg);
262 
263  if(!loc_var.empty()) {
264  config &temp = res_.add_child("event");
265  temp["name"] = "prestart";
266  config &xcfg = temp.add_child("set_variable");
267  xcfg["name"] = loc_var + "_x";
268  xcfg["value"] = loc->x + 1;
269  config &ycfg = temp.add_child("set_variable");
270  ycfg["name"] = loc_var + "_y";
271  ycfg["value"] = loc->y + 1;
272  }
273  }
274 }
275 
277 {
279  const t_translation::terrain_code & wall,
280  double laziness, size_t windiness,
281  std::mt19937& rng) :
282  map_(mapdata), wall_(wall), laziness_(laziness), windiness_(windiness), rng_(rng)
283  {}
284 
285  virtual double cost(const map_location& loc, const double so_far) const;
286 private:
289  double laziness_;
290  size_t windiness_;
291  std::mt19937& rng_;
292 };
293 
294 double passage_path_calculator::cost(const map_location& loc, const double) const
295 {
296  double res = 1.0;
297  if (map_.get(loc.x + gamemap::default_border, loc.y + gamemap::default_border) == wall_) {
298  res = laziness_;
299  }
300 
301  if(windiness_ > 1) {
302  res *= double(rng_()%windiness_);
303  }
304 
305  return res;
306 }
307 
309 {
310  const std::string& chance = p.cfg["chance"];
311  if(!chance.empty() && int(rng_()%100) < std::stoi(chance)) {
312  return;
313  }
314 
315 
316  int windiness = p.cfg["windiness"];
317  double laziness = std::max<double>(1.0, p.cfg["laziness"].to_double());
318 
319  passage_path_calculator calc(map_, params.wall_, laziness, windiness, rng_);
320 
321  pathfind::plain_route rt = a_star_search(p.src, p.dst, 10000.0, calc, params.width_, params.height_);
322 
323  int width = std::max<int>(1, p.cfg["width"].to_int());
324  int jagged = p.cfg["jagged"];
325 
326  for(std::vector<map_location>::const_iterator i = rt.steps.begin(); i != rt.steps.end(); ++i) {
327  std::set<map_location> locs;
328  build_chamber(*i,locs,width,jagged);
329  for(std::set<map_location>::const_iterator j = locs.begin(); j != locs.end(); ++j) {
330  set_terrain(*j, params.clear_);
331  }
332  }
333 }
334 
336 {
337  if (params.on_board(loc)) {
339 
340  if(c == params.clear_ || c == params.wall_ || c == params.village_) {
341  // Change this terrain.
342  if ( t == params.clear_ && int(rng_() % 1000) < params.village_density_ )
343  // Override with a village.
344  c = params.village_;
345  else
346  c = t;
347  }
348  }
349 }
350 
352 {
353  if (starting_position != -1) {
354  set_terrain(loc, params.keep_);
355 
358  , loc.y + gamemap::default_border);
359  starting_positions_.insert(t_translation::starting_positions::value_type(std::to_string(starting_position), coord));
360  }
361 
362  map_location adj[6];
363  get_adjacent_tiles(loc,adj);
364  for(size_t n = 0; n != 6; ++n) {
365  set_terrain(adj[n], params.castle_);
366  }
367 }
368 
const t_translation::ter_map & map_
const terrain_code CAVE
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:363
std::vector< char_t > string
size_t index(const utf8::string &str, const size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
const terrain_code DWARVEN_KEEP
std::string config_name() const
Return a friendly name for the generator used to differentiate between different configs of the same ...
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.hpp:299
child_itors child_range(config_key_type key)
Definition: config.cpp:306
static const int default_border
The default border style for a map.
Definition: map.hpp:180
std::string create_map(boost::optional< uint32_t > randomseed=boost::none)
Creates a new map and returns it.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:55
t_translation::terrain_code wall_
const std::vector< std::string > items
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
t_translation::terrain_code wall_
std::vector< map_location > steps
Definition: pathfind.hpp:134
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:131
const terrain_code DWARVEN_CASTLE
Encapsulates the map of the game.
Definition: map.hpp:34
config create_scenario(boost::optional< uint32_t > randomseed=boost::none)
cave_map_generator_job(const cave_map_generator &params, boost::optional< uint32_t > randomseed=boost::none)
passage_path_calculator(const t_translation::ter_map &mapdata, const t_translation::terrain_code &wall, double laziness, size_t windiness, std::mt19937 &rng)
virtual double cost(const map_location &loc, const double so_far) const
void place_castle(int starting_position, const map_location &loc)
Encapsulates the map of the game.
Definition: location.hpp:40
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const size_t width, const size_t height, const teleport_map *teleports, bool border)
size_t size(const utf8::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
void build_chamber(map_location loc, std::set< map_location > &locs, size_t size, size_t jagged)
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:269
mock_party p
static lg::log_domain log_engine("engine")
std::string write_game_map(const ter_map &map, const starting_positions &starting_positions, coordinate border_offset)
Write a gamemap in to a vector string.
size_t i
Definition: function.cpp:933
const terrain_code CAVE_WALL
config & add_child(config_key_type key)
Definition: config.cpp:419
uint32_t next_seed()
Definition: seed_rng.cpp:45
double t
Definition: astarsearch.cpp:64
const terrain_code UNDERGROUND_VILLAGE
Standard logging facilities (interface).
void set_terrain(map_location loc, const t_translation::terrain_code &t)
#define LOG_NG
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
mock_char c
cave_map_generator(const config &game_config)
static map_location::DIRECTION n
This module contains various pathfinding functions and utilities.
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:798
t_translation::starting_positions starting_positions_