The Battle for Wesnoth  1.19.18+dev
type_data.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2025
3  by Chris Beck <render787@gmail.com>
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 #include "terrain/type_data.hpp"
17 
19 #include "game_config_view.hpp"
20 
21 #include <map>
22 
23 #include "log.hpp"
24 #define ERR_G LOG_STREAM(err, lg::general())
25 #define LOG_G LOG_STREAM(info, lg::general())
26 #define DBG_G LOG_STREAM(debug, lg::general())
27 
28 std::shared_ptr<terrain_type_data> terrain_type_data::reset(const game_config_view& game_config)
29 {
31  return singleton_;
32 }
33 
34 std::shared_ptr<terrain_type_data> terrain_type_data::get()
35 {
36  return singleton_;
37 }
38 
40  : terrainList_()
41  , tcodeToTerrain_()
42  , initialized_(false)
43  , game_config_(game_config)
44 {
45 }
46 
48 {
49  if(initialized_)
50  return;
51 
52  for (const config &terrain_data : game_config_.child_range("terrain_type"))
53  {
54  terrain_type terrain(terrain_data);
55  DBG_G << "create_terrain_maps: " << terrain.number() << " "
56  << terrain.id() << " " << terrain.name() << " : " << terrain.editor_group();
57 
59  res = tcodeToTerrain_.emplace(terrain.number(), terrain);
60  if (!res.second) {
61  terrain_type& curr = res.first->second;
62  if(terrain == curr) {
63  LOG_G << "Merging terrain " << terrain.number()
64  << ": " << terrain.id() << " (" << terrain.name() << ")";
65  std::vector<std::string> eg1 = utils::split(curr.editor_group());
66  std::vector<std::string> eg2 = utils::split(terrain.editor_group());
67  std::set<std::string> egs;
68  bool clean_merge = true;
69  for (std::string& t : eg1) {
70  clean_merge &= egs.insert(t).second;
71  }
72  for (std::string& t : eg2) {
73  clean_merge &= egs.insert(t).second;
74  }
75  std::string joined = utils::join(egs);
76 
77  if(clean_merge) {
78  LOG_G << "Editor groups merged to: " << joined;
79  } else {
80  LOG_G << "Merged terrain " << terrain.number()
81  << ": " << terrain.id() << " (" << terrain.name() << ") "
82  << "with duplicate editor groups [" << terrain.editor_group() << "] "
83  << "and [" << curr.editor_group() << "]";
84  }
85  curr.set_editor_group(joined);
86  } else {
87  ERR_G << "Duplicate terrain code definition found for " << terrain.number() << "\n"
88  << "Failed to add terrain " << terrain.id() << " (" << terrain.name() << ") "
89  << "[" << terrain.editor_group() << "]" << "\n"
90  << "which conflicts with " << curr.id() << " (" << curr.name() << ") "
91  << "[" << curr.editor_group() << "]" << "\n";
92  }
93  } else {
94  terrainList_.push_back(terrain.number());
95  }
96  }
97  initialized_ = true;
98 }
99 
101 {
103  return terrainList_;
104 }
105 
106 
107 const std::map<t_translation::terrain_code, terrain_type> & terrain_type_data::map() const
108 {
110  return tcodeToTerrain_;
111 }
112 
114 {
115  auto i = find_or_create(terrain);
116 
117  if(i != tcodeToTerrain_.end()) {
118  return i->second;
119  } else {
120  static const terrain_type default_terrain;
121  return default_terrain;
122  }
123 }
124 
126 {
127  auto i = find_or_create(terrain);
128 
129  if(i == tcodeToTerrain_.end()) {
130  // TODO: At least in some cases (for example when this is called from lua) it
131  // seems to make more sense to throw an exception here, same goes for get_terrain_info
132  // and underlying_def_terrain
133  static t_translation::ter_list result(1);
134  result[0] = terrain;
135  return result;
136  } else {
137  return i->second.mvt_type();
138  }
139 }
140 
142 {
143  auto i = find_or_create(terrain);
144 
145  if(i == tcodeToTerrain_.end()) {
146  static t_translation::ter_list result(1);
147  result[0] = terrain;
148  return result;
149  } else {
150  return i->second.def_type();
151  }
152 }
153 
155 {
156  auto i = find_or_create(terrain);
157 
158  if(i == tcodeToTerrain_.end()) {
159  static t_translation::ter_list result(1);
160  result[0] = terrain;
161  return result;
162  } else {
163  return i->second.union_type();
164  }
165 }
166 
167 
168 
170 {
171  t_string str =
172  get_terrain_info(terrain).description();
173 
174  str += get_underlying_terrain_string(terrain);
175 
176  return str;
177 }
178 
180 {
181  t_string str =
182  get_terrain_info(terrain).editor_name();
183  const t_string& desc =
184  get_terrain_info(terrain).description();
185 
186  if(str != desc) {
187  str += "/";
188  str += desc;
189  }
190 
191  str += get_underlying_terrain_string(terrain);
192 
193  return str;
194 }
195 
197 {
198  // lazy_initialization() is handled in underlying_union_terrain
199  std::string str;
200 
201  const t_translation::ter_list& underlying = underlying_union_terrain(terrain);
202  assert(!underlying.empty());
203 
204  if(underlying.size() > 1 || underlying[0] != terrain) {
205  str += " (";
206  t_translation::ter_list::const_iterator i = underlying.begin();
207  str += get_terrain_info(*i).name();
208  while (++i != underlying.end()) {
209  str += ", " + get_terrain_info(*i).name();
210  }
211  str += ")";
212  }
213 
214  return str;
215 }
216 
217 terrain_type_data::tcodeToTerrain_t::const_iterator terrain_type_data::find_or_create(t_translation::terrain_code terrain) const
218 {
220  auto i = tcodeToTerrain_.find(terrain);
221  if (i != tcodeToTerrain_.end()) {
222  return i;
223  }
224  else {
225  DBG_G << "find_or_create: creating terrain " << terrain;
226  auto base_iter = tcodeToTerrain_.find(t_translation::terrain_code(terrain.base, t_translation::NO_LAYER));
227  auto overlay_iter = tcodeToTerrain_.find(t_translation::terrain_code(t_translation::NO_LAYER, terrain.overlay));
228 
229  if(base_iter == tcodeToTerrain_.end() || overlay_iter == tcodeToTerrain_.end()) {
230  // This line is easily reachable, after the player has played multiple
231  // campaigns. The code for showing movetypes for discovered terrains in the
232  // sidebar will query every terrain listed in
233  // prefs::get().encountered_terrains(), even those that are campaign-specific.
234  // ERR_G << "couldn't find base or overlay for " << terrain;
235  return tcodeToTerrain_.end();
236  }
237 
238  terrain_type new_terrain(base_iter->second, overlay_iter->second);
239  terrainList_.push_back(new_terrain.number());
240  return tcodeToTerrain_.emplace(new_terrain.number(), std::move(new_terrain)).first;
241  }
242 }
243 
245 {
246  // These can't be combined in to a single line, because find_or_create
247  // can change the value of tcodeToTerrain_.end().
248  const auto t = find_or_create(terrain);
249  return t != tcodeToTerrain_.end();
250 }
251 
254 
255  if(mode == OVERLAY) {
257  if (is_known(t)) {
258  result = t;
259  }
260  }
261  else if(mode == BASE) {
263  if (is_known(t)) {
264  result = t;
265  }
266  }
267  else if(mode == BOTH && new_t.base != t_translation::NO_LAYER) {
268  // We need to merge here, too, because the dest terrain might be a combined one.
269  if (is_known(new_t)) {
270  result = new_t;
271  }
272  }
273 
274  // if merging of overlay and base failed, and replace_if_failed is set,
275  // replace the terrain with the complete new terrain (if given)
276  // or with (default base)^(new overlay)
277  if(result == t_translation::NONE_TERRAIN && replace_if_failed && is_known(new_t)) {
278  if(new_t.base != t_translation::NO_LAYER) {
279  result = new_t;
280  }
281  else if (get_terrain_info(new_t).has_default_base()) {
283  }
284  }
285  return result;
286 }
map_location curr
Definition: astarsearch.cpp:64
double t
Definition: astarsearch.cpp:63
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
A class grating read only view to a vector of config objects, viewed as one config with all children ...
config_array_view child_range(std::string_view key) const
static std::shared_ptr< terrain_type_data > get()
Definition: type_data.cpp:34
t_translation::ter_list terrainList_
Definition: type_data.hpp:43
static std::shared_ptr< terrain_type_data > reset(const game_config_view &game_config)
Regenerates the terrain database from the given game config.
Definition: type_data.cpp:28
void lazy_initialization() const
On the first call to this function, parse all of the [terrain_type]s that are defined in WML.
Definition: type_data.cpp:47
t_string get_underlying_terrain_string(const t_translation::terrain_code &terrain) const
Definition: type_data.cpp:196
const t_translation::ter_list & underlying_def_terrain(const t_translation::terrain_code &terrain) const
The underlying defense type of the terrain.
Definition: type_data.cpp:141
t_string get_terrain_string(const t_translation::terrain_code &terrain) const
Get a formatted terrain name – terrain (underlying terrains)
Definition: type_data.cpp:169
tcodeToTerrain_t tcodeToTerrain_
Definition: type_data.hpp:45
const game_config_view & game_config_
Definition: type_data.hpp:47
bool is_known(const t_translation::terrain_code &terrain) const
Returns true if get_terrain_info(terrain) would succeed, or false if get_terrain_info(terrain) would ...
Definition: type_data.cpp:244
const t_translation::ter_list & underlying_mvt_terrain(const t_translation::terrain_code &terrain) const
The underlying movement type of the terrain.
Definition: type_data.cpp:125
t_translation::terrain_code merge_terrains(const t_translation::terrain_code &old_t, const t_translation::terrain_code &new_t, const merge_mode mode, bool replace_if_failed=false) const
Tries to find a new terrain which is the combination of old and new terrain using the merge_settings.
Definition: type_data.cpp:252
const std::map< t_translation::terrain_code, terrain_type > & map() const
Definition: type_data.cpp:107
tcodeToTerrain_t::const_iterator find_or_create(t_translation::terrain_code) const
Definition: type_data.cpp:217
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Get the corresponding terrain_type information object for a given type of terrain.
Definition: type_data.cpp:113
const t_translation::ter_list & underlying_union_terrain(const t_translation::terrain_code &terrain) const
Unordered set of all terrains used in either underlying_mvt_terrain or underlying_def_terrain.
Definition: type_data.cpp:154
t_string get_terrain_editor_string(const t_translation::terrain_code &terrain) const
Definition: type_data.cpp:179
terrain_type_data(const game_config_view &game_config)
Definition: type_data.cpp:39
static std::shared_ptr< terrain_type_data > singleton_
Definition: type_data.hpp:167
const t_translation::ter_list & list() const
Definition: type_data.cpp:100
const std::string & editor_group() const
Definition: terrain.hpp:155
const t_string & name() const
Definition: terrain.hpp:48
t_translation::terrain_code terrain_with_default_base() const
Return the overlay part of this terrain, on the default_base().
Definition: terrain.cpp:286
const std::string & id() const
Definition: terrain.hpp:52
const t_string & description() const
Definition: terrain.hpp:50
const t_string & editor_name() const
Definition: terrain.hpp:49
t_translation::terrain_code number() const
Definition: terrain.hpp:66
std::size_t i
Definition: function.cpp:1032
Standard logging facilities (interface).
Game configuration data as global variables.
Definition: build_info.cpp:61
std::string default_terrain
Definition: game_config.cpp:57
constexpr terrain_code NONE_TERRAIN
Definition: translation.hpp:58
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
constexpr ter_layer NO_LAYER
Definition: translation.hpp:40
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
std::vector< std::string > split(const config_attribute_value &val)
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
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
#define ERR_G
Definition: type_data.cpp:24
#define LOG_G
Definition: type_data.cpp:25
#define DBG_G
Definition: type_data.cpp:26