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