The Battle for Wesnoth  1.15.0-dev
cave_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 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  "deprecated_message", config {
99  "what", "scenario_generation=cave",
100  "level", 1,
101  "message", "Use the Lua cave generator instead, with scenario_generation=lua and create_scenario= (see wiki for details).",
102  },
103  });
104  uint32_t seed = randomseed.get_ptr() ? *randomseed.get_ptr() : seed_rng::next_seed();
105  rng_.seed(seed);
106  LOG_NG << "creating random cave with seed: " << seed << '\n';
107  flipx_ = static_cast<int>(rng_() % 100) < params.flipx_chance_;
108  flipy_ = static_cast<int>(rng_() % 100) < params.flipy_chance_;
109 
110  LOG_NG << "creating scenario....\n";
112 
113  LOG_NG << "placing chambers...\n";
114  for(std::vector<chamber>::const_iterator c = chambers_.begin(); c != chambers_.end(); ++c) {
115  place_chamber(*c);
116  }
117 
118  LOG_NG << "placing passages...\n";
119 
120  for(std::vector<passage>::const_iterator p = passages_.begin(); p != passages_.end(); ++p) {
121  place_passage(*p);
122  }
123  LOG_NG << "outputting map....\n";
124 
126 }
127 
128 void cave_map_generator::cave_map_generator_job::build_chamber(map_location loc, std::set<map_location>& locs, std::size_t size, std::size_t jagged)
129 {
130  if(size == 0 || locs.count(loc) != 0 || !params.on_board(loc))
131  return;
132 
133  locs.insert(loc);
134 
136  get_adjacent_tiles(loc,adj.data());
137  for(std::size_t n = 0; n < adj.size(); ++n) {
138  if(static_cast<int>(rng_() % 100) < (100l - static_cast<long>(jagged))) {
139  build_chamber(adj[n],locs,size-1,jagged);
140  }
141  }
142 }
143 
145 {
146  for (const config &ch : params.cfg_.child_range("chamber"))
147  {
148  // If there is only a chance of the chamber appearing, deal with that here.
149  if (ch.has_attribute("chance") && static_cast<int>(rng_() % 100) < ch["chance"].to_int()) {
150  continue;
151  }
152 
153  const std::string &xpos = ch["x"];
154  const std::string &ypos = ch["y"];
155 
156  std::size_t min_xpos = 0, min_ypos = 0, max_xpos = params.width_, max_ypos = params.height_;
157 
158  if (!xpos.empty()) {
159  const std::vector<std::string>& items = utils::split(xpos, '-');
160  if(items.empty() == false) {
161  try {
162  min_xpos = std::stoi(items.front()) - 1;
163  max_xpos = std::stoi(items.back());
164  } catch(const std::invalid_argument&) {
165  lg::wml_error() << "Invalid min/max coordinates in cave_map_generator: " << items.front() << ", " << items.back() << "\n";
166  continue;
167  }
168  }
169  }
170 
171  if (!ypos.empty()) {
172  const std::vector<std::string>& items = utils::split(ypos, '-');
173  if(items.empty() == false) {
174  try {
175  min_ypos = std::stoi(items.front()) - 1;
176  max_ypos = std::stoi(items.back());
177  } catch(const std::invalid_argument&) {
178  lg::wml_error() << "Invalid min/max coordinates in cave_map_generator: " << items.front() << ", " << items.back() << "\n";
179  }
180  }
181  }
182  const std::size_t x = translate_x(min_xpos + (rng_()%(max_xpos-min_xpos)));
183  const std::size_t y = translate_y(min_ypos + (rng_()%(max_ypos-min_ypos)));
184 
185  int chamber_size = ch["size"].to_int(3);
186  int jagged_edges = ch["jagged"];
187 
188  chamber new_chamber;
189  new_chamber.center = map_location(x,y);
190  build_chamber(new_chamber.center,new_chamber.locs,chamber_size,jagged_edges);
191 
192  const config &items = ch.child("items");
193  new_chamber.items = items ? &items : nullptr;
194 
195  const std::string &id = ch["id"];
196  if (!id.empty()) {
197  chamber_ids_[id] = chambers_.size();
198  }
199 
200  chambers_.push_back(new_chamber);
201 
202  for(const config &p : ch.child_range("passage"))
203  {
204  const std::string &dst = p["destination"];
205 
206  // Find the destination of this passage
207  const std::map<std::string,std::size_t>::const_iterator itor = chamber_ids_.find(dst);
208  if(itor == chamber_ids_.end())
209  continue;
210 
211  assert(itor->second < chambers_.size());
212 
213  passages_.emplace_back(new_chamber.center, chambers_[itor->second].center, p);
214  }
215  }
216 }
217 
219 {
220  for(std::set<map_location>::const_iterator i = c.locs.begin(); i != c.locs.end(); ++i) {
222  }
223 
224  if (c.items == nullptr || c.locs.empty()) return;
225 
226  std::size_t index = 0;
227  for (const config::any_child &it : c.items->all_children_range())
228  {
229  config cfg = it.cfg;
230  config &filter = cfg.child("filter");
231  config* object_filter = nullptr;
232  if (config &object = cfg.child("object")) {
233  if (config &of = object.child("filter"))
234  object_filter = &of;
235  }
236 
237  if (!it.cfg["same_location_as_previous"].to_bool()) {
238  index = rng_()%c.locs.size();
239  }
240  std::string loc_var = it.cfg["store_location_as"];
241 
242  std::set<map_location>::const_iterator loc = c.locs.begin();
243  std::advance(loc,index);
244 
245  cfg["x"] = loc->x + 1;
246  cfg["y"] = loc->y + 1;
247 
248  if (filter) {
249  filter["x"] = loc->x + 1;
250  filter["y"] = loc->y + 1;
251  }
252 
253  if (object_filter) {
254  (*object_filter)["x"] = loc->x + 1;
255  (*object_filter)["y"] = loc->y + 1;
256  }
257 
258  // If this is a side, place a castle for the side
259  if (it.key == "side" && !it.cfg["no_castle"].to_bool()) {
260  place_castle(it.cfg["side"].to_int(-1), *loc);
261  }
262 
263  res_.add_child(it.key, cfg);
264 
265  if(!loc_var.empty()) {
266  config &temp = res_.add_child("event");
267  temp["name"] = "prestart";
268  config &xcfg = temp.add_child("set_variable");
269  xcfg["name"] = loc_var + "_x";
270  xcfg["value"] = loc->x + 1;
271  config &ycfg = temp.add_child("set_variable");
272  ycfg["name"] = loc_var + "_y";
273  ycfg["value"] = loc->y + 1;
274  }
275  }
276 }
277 
279 {
281  const t_translation::terrain_code & wall,
282  double laziness, std::size_t windiness,
283  std::mt19937& rng) :
284  map_(mapdata), wall_(wall), laziness_(laziness), windiness_(windiness), rng_(rng)
285  {}
286 
287  virtual double cost(const map_location& loc, const double so_far) const;
288 private:
291  double laziness_;
292  std::size_t windiness_;
293  std::mt19937& rng_;
294 };
295 
296 double passage_path_calculator::cost(const map_location& loc, const double) const
297 {
298  double res = 1.0;
300  res = laziness_;
301  }
302 
303  if(windiness_ > 1) {
304  res *= static_cast<double>(rng_()%windiness_);
305  }
306 
307  return res;
308 }
309 
311 {
312  const std::string& chance = p.cfg["chance"];
313  if(!chance.empty() && static_cast<int>(rng_()%100) < std::stoi(chance)) {
314  return;
315  }
316 
317 
318  int windiness = p.cfg["windiness"];
319  double laziness = std::max<double>(1.0, p.cfg["laziness"].to_double());
320 
321  passage_path_calculator calc(map_, params.wall_, laziness, windiness, rng_);
322 
324 
325  int width = std::max<int>(1, p.cfg["width"].to_int());
326  int jagged = p.cfg["jagged"];
327 
328  for(std::vector<map_location>::const_iterator i = rt.steps.begin(); i != rt.steps.end(); ++i) {
329  std::set<map_location> locs;
330  build_chamber(*i,locs,width,jagged);
331  for(std::set<map_location>::const_iterator j = locs.begin(); j != locs.end(); ++j) {
333  }
334  }
335 }
336 
338 {
339  if (params.on_board(loc)) {
341 
342  if(c == params.clear_ || c == params.wall_ || c == params.village_) {
343  // Change this terrain.
344  if ( t == params.clear_ && static_cast<int>(rng_() % 1000) < params.village_density_ )
345  // Override with a village.
346  c = params.village_;
347  else
348  c = t;
349  }
350  }
351 }
352 
354 {
355  if (starting_position != -1) {
356  set_terrain(loc, params.keep_);
357 
360  , loc.y + gamemap::default_border);
361  starting_positions_.insert(t_translation::starting_positions::value_type(std::to_string(starting_position), coord));
362  }
363 
365  get_adjacent_tiles(loc,adj.data());
366  for(std::size_t n = 0; n < adj.size(); ++n) {
367  set_terrain(adj[n], params.castle_);
368  }
369 }
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:423
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:874
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.cpp:517
const terrain_code DWARVEN_KEEP
child_itors child_range(config_key_type key)
Definition: config.cpp:366
t_translation::terrain_code village_
static const int default_border
The default border style for a map.
Definition: map.hpp:180
virtual double cost(const map_location &loc, const double so_far) const
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:49
t_translation::terrain_code wall_
t_translation::terrain_code clear_
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::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
std::vector< map_location > steps
Definition: pathfind.hpp:134
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:131
t_translation::terrain_code keep_
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)
std::size_t translate_y(std::size_t y) const
std::array< map_location, 6 > adjacent_loc_array_t
Definition: location.hpp:170
void place_castle(int starting_position, const map_location &loc)
Encapsulates the map of the game.
Definition: location.hpp:42
std::size_t i
Definition: function.cpp:933
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")
bool on_board(const map_location &loc) const
std::string config_name() const
Return a friendly name for the generator used to differentiate between different configs of the same ...
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
const terrain_code CAVE_WALL
terrain_code & get(int x, int y)
Definition: translation.hpp:89
config & add_child(config_key_type key)
Definition: config.cpp:479
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.
std::map< std::string, std::size_t > chamber_ids_
uint32_t next_seed()
Definition: seed_rng.cpp:45
double t
Definition: astarsearch.cpp:63
const terrain_code UNDERGROUND_VILLAGE
t_translation::terrain_code castle_
Standard logging facilities (interface).
passage_path_calculator(const t_translation::ter_map &mapdata, const t_translation::terrain_code &wall, double laziness, std::size_t windiness, std::mt19937 &rng)
void set_terrain(map_location loc, const t_translation::terrain_code &t)
#define LOG_NG
void build_chamber(map_location loc, std::set< map_location > &locs, std::size_t size, std::size_t jagged)
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const std::size_t width, const std::size_t height, const teleport_map *teleports, bool border)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
mock_char c
cave_map_generator(const config &game_config)
static map_location::DIRECTION n
This module contains various pathfinding functions and utilities.
t_translation::starting_positions starting_positions_
std::size_t translate_x(std::size_t x) const