The Battle for Wesnoth  1.17.23+dev
terrain.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
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 #include "deprecation.hpp"
17 #include "game_version.hpp"
18 #include "gettext.hpp"
19 #include "log.hpp"
21 #include "terrain/terrain.hpp"
22 
23 #include <set>
24 
25 static lg::log_domain log_config("config");
26 #define ERR_G LOG_STREAM(err, lg::general())
27 #define WRN_G LOG_STREAM(warn, lg::general())
28 #define LOG_G LOG_STREAM(info, lg::general())
29 #define DBG_G LOG_STREAM(debug, lg::general())
30 
31 /**
32  * Insert second vector into first when the terrain _ref^base is encountered
33  */
35 
37  minimap_image_(),
38  minimap_image_overlay_(),
39  editor_image_(),
40  id_(),
41  name_(),
42  editor_name_(),
43  description_(),
44  help_topic_text_(),
45  number_(t_translation::VOID_TERRAIN),
46  mvt_type_(1, t_translation::VOID_TERRAIN),
47  vision_type_(1, t_translation::VOID_TERRAIN),
48  def_type_(1, t_translation::VOID_TERRAIN),
49  union_type_(1, t_translation::VOID_TERRAIN),
50  height_adjust_(0),
51  height_adjust_set_(false),
52  submerge_(0.0),
53  submerge_set_(false),
54  light_modification_(0),
55  max_light_(0),
56  min_light_(0),
57  heals_(0),
58  income_description_(),
59  income_description_ally_(),
60  income_description_enemy_(),
61  income_description_own_(),
62  editor_group_(),
63  village_(false),
64  castle_(false),
65  keep_(false),
66  overlay_(false),
67  combined_(false),
68  editor_default_base_(t_translation::VOID_TERRAIN),
69  hide_help_(false),
70  hide_in_editor_(false),
71  hide_if_impassable_(false)
72 {}
73 
75  icon_image_(cfg["icon_image"]),
76  minimap_image_(cfg["symbol_image"]),
77  minimap_image_overlay_(),
78  editor_image_(cfg["editor_image"].empty() ? "terrain/" + minimap_image_ + ".png" : "terrain/" + cfg["editor_image"].str() + ".png"),
79  id_(cfg["id"]),
80  name_(cfg["name"].t_str()),
81  editor_name_(cfg["editor_name"].t_str()),
82  description_(cfg["description"].t_str()),
83  help_topic_text_(cfg["help_topic_text"].t_str()),
84  number_(t_translation::read_terrain_code(cfg["string"].str())),
85  mvt_type_(),
86  vision_type_(),
87  def_type_(),
88  union_type_(),
89  height_adjust_(cfg["unit_height_adjust"]),
90  height_adjust_set_(!cfg["unit_height_adjust"].empty()),
91  submerge_(cfg["submerge"].to_double()),
92  submerge_set_(!cfg["submerge"].empty()),
93  light_modification_(cfg["light"]),
94  max_light_(cfg["max_light"].to_int(light_modification_)),
95  min_light_(cfg["min_light"].to_int(light_modification_)),
96  heals_(cfg["heals"]),
97  income_description_(),
98  income_description_ally_(),
99  income_description_enemy_(),
100  income_description_own_(),
101  editor_group_(cfg["editor_group"]),
102  village_(cfg["gives_income"].to_bool()),
103  castle_(cfg["recruit_onto"].to_bool()),
104  keep_(cfg["recruit_from"].to_bool()),
105  overlay_(number_.base == t_translation::NO_LAYER),
106  combined_(false),
107  editor_default_base_(t_translation::read_terrain_code(cfg["default_base"].str())),
108  hide_help_(cfg["hide_help"].to_bool(false)),
109  hide_in_editor_(cfg["hidden"].to_bool(false)),
110  hide_if_impassable_(cfg["hide_if_impassable"].to_bool(false))
111 {
112 /**
113  * @todo reenable these validations. The problem is that all MP
114  * scenarios/campaigns share the same namespace and one rogue scenario
115  * can avoid the player to create a MP game. So every scenario/campaign
116  * should get its own namespace to be safe.
117  */
118 #if 0
120  missing_mandatory_wml_key("terrain_type", "string"));
121  VALIDATE(!minimap_image_.empty(),
122  missing_mandatory_wml_key("terrain_type", "symbol_image", "string",
124  VALIDATE(!name_.empty(),
125  missing_mandatory_wml_key("terrain_type", "name", "string",
127 #endif
128 
129  if(editor_image_.empty()) {
130  editor_image_ = "terrain/" + minimap_image_ + ".png";
131  }
132 
133  if(hide_in_editor_) {
134  editor_image_ = "";
135  }
136 
137  mvt_type_.push_back(number_);
138  def_type_.push_back(number_);
139  vision_type_.push_back(number_);
140 
141  const t_translation::ter_list& alias = t_translation::read_list(cfg["aliasof"].str());
142  if(!alias.empty()) {
143  mvt_type_ = alias;
144  vision_type_ = alias;
145  def_type_ = alias;
146  }
147 
148  const t_translation::ter_list& mvt_alias = t_translation::read_list(cfg["mvt_alias"].str());
149  if(!mvt_alias.empty()) {
150  mvt_type_ = mvt_alias;
151  }
152 
153  const t_translation::ter_list& def_alias = t_translation::read_list(cfg["def_alias"].str());
154  if(!def_alias.empty()) {
155  def_type_ = def_alias;
156  }
157 
158  const t_translation::ter_list& vision_alias = t_translation::read_list(cfg["vision_alias"].str());
159  if(!vision_alias.empty()) {
160  // Vision costs are calculated in movetype.cpp, but they're calculated based on gamemap::underlying_mvt_terrain().
161  // Having vision costs that are different to movement costs is still supported, but having separate aliases seems
162  // an edge case that shouldn't be introduced until we're ready to test it.
163  deprecated_message("vision_alias", DEP_LEVEL::REMOVED, {1, 15, 2}, "vision_alias was never completely implemented, vision is calculated using mvt_alias instead");
164  vision_type_ = vision_alias;
165  }
166 
168  union_type_.insert( union_type_.end(), def_type_.begin(), def_type_.end() );
169  union_type_.insert( union_type_.end(), vision_type_.begin(), vision_type_.end() );
170 
171  // remove + and -
172  union_type_.erase(std::remove(union_type_.begin(), union_type_.end(),
174 
175  union_type_.erase(std::remove(union_type_.begin(), union_type_.end(),
177 
178  // remove doubles
179  std::sort(union_type_.begin(),union_type_.end());
180  union_type_.erase(std::unique(union_type_.begin(), union_type_.end()), union_type_.end());
181 
182 
183 
184  //mouse over message are only shown on villages
185  if(village_) {
186  income_description_ = cfg["income_description"];
187  if(income_description_.empty()) {
188  income_description_ = _("Village");
189  }
190 
191  income_description_ally_ = cfg["income_description_ally"];
193  income_description_ally_ = _("Allied village");
194  }
195 
196  income_description_enemy_ = cfg["income_description_enemy"];
198  income_description_enemy_ = _("Enemy village");
199  }
200 
201  income_description_own_ = cfg["income_description_own"];
203  income_description_own_ = _("Owned village");
204  }
205  }
206 }
207 
209  icon_image_(),
210  minimap_image_(base.minimap_image_),
211  minimap_image_overlay_(overlay.minimap_image_),
212  editor_image_(base.editor_image_ + "~BLIT(" + overlay.editor_image_ +")"),
213  id_(base.id_+"^"+overlay.id_),
214  name_(overlay.name_),
215  editor_name_((base.editor_name_.empty() ? base.name_ : base.editor_name_) + " / " + (overlay.editor_name_.empty() ? overlay.name_ : overlay.editor_name_)),
216  description_(overlay.description()),
217  help_topic_text_(),
218  number_(t_translation::terrain_code(base.number_.base, overlay.number_.overlay)),
219  mvt_type_(overlay.mvt_type_),
220  vision_type_(overlay.vision_type_),
221  def_type_(overlay.def_type_),
222  union_type_(),
223  height_adjust_(base.height_adjust_),
224  height_adjust_set_(base.height_adjust_set_),
225  submerge_(base.submerge_),
226  submerge_set_(base.submerge_set_),
227  light_modification_(base.light_modification_ + overlay.light_modification_),
228  max_light_(std::max(base.max_light_, overlay.max_light_)),
229  min_light_(std::min(base.min_light_, overlay.min_light_)),
230  heals_(std::max<int>(base.heals_, overlay.heals_)),
231  income_description_(),
232  income_description_ally_(),
233  income_description_enemy_(),
234  income_description_own_(),
235  editor_group_(),
236  village_(base.village_ || overlay.village_),
237  castle_(base.castle_ || overlay.castle_),
238  keep_(base.keep_ || overlay.keep_),
239  overlay_(false),
240  combined_(true),
241  editor_default_base_(),
242  hide_help_(true),
243  hide_in_editor_(base.hide_in_editor_ || overlay.hide_in_editor_),
244  hide_if_impassable_(base.hide_if_impassable_ || overlay.hide_if_impassable_)
245 {
246  if(description_.empty()) {
247  description_ = base.description();
248  }
249 
250  if(overlay.height_adjust_set_) {
251  height_adjust_set_ = true;
252  height_adjust_ = overlay.height_adjust_;
253  }
254 
255  if(overlay.submerge_set_) {
256  submerge_set_ = true;
257  submerge_ = overlay.submerge_;
258  }
259 
263 
265  union_type_.insert( union_type_.end(), def_type_.begin(), def_type_.end() );
266  union_type_.insert( union_type_.end(), vision_type_.begin(), vision_type_.end() );
267 
268  // remove + and -
269  union_type_.erase(std::remove(union_type_.begin(), union_type_.end(),
271 
272  union_type_.erase(std::remove(union_type_.begin(), union_type_.end(),
274 
275  // remove doubles
276  std::sort(union_type_.begin(),union_type_.end());
277  union_type_.erase(std::unique(union_type_.begin(), union_type_.end()), union_type_.end());
278 
279 
280 
281  //mouse over message are only shown on villages
282  if(base.village_) {
287  }
288  else if (overlay.village_) {
289  income_description_ = overlay.income_description_;
290  income_description_ally_ = overlay.income_description_ally_;
291  income_description_enemy_ = overlay.income_description_enemy_;
292  income_description_own_ = overlay.income_description_own_;
293  }
294 
295 }
296 
300  }
301  return number_;
302 }
303 
304 bool terrain_type::operator==(const terrain_type& other) const {
305  return minimap_image_ == other.minimap_image_
307  && editor_image_ == other.editor_image_
308  && id_ == other.id_
309  && name_.base_str() == other.name_.base_str()
311  && number_ == other.number_
312  && mvt_type_ == other.mvt_type_
313  && vision_type_ == other.vision_type_
314  && def_type_ == other.def_type_
315  && union_type_ == other.union_type_
316  && height_adjust_ == other.height_adjust_
318  && submerge_ == other.submerge_
319  && submerge_set_ == other.submerge_set_
321  && max_light_ == other.max_light_
322  && min_light_ == other.min_light_
323  && heals_ == other.heals_
324  && village_ == other.village_
325  && castle_ == other.castle_
326  && keep_ == other.keep_
327  && combined_ == other.combined_
328  && overlay_ == other.overlay_
330  && hide_in_editor_ == other.hide_in_editor_
331  && hide_help_ == other.hide_help_;
332 }
333 
335 {
336  // Insert second vector into first when the terrain _ref^base is encountered
337 
338  bool revert = (first.front() == t_translation::MINUS ? true : false);
340 
341  for(i = first.begin(); i != first.end(); ++i) {
342  if(*i == t_translation::PLUS) {
343  revert = false;
344  continue;
345  } else if(*i == t_translation::MINUS) {
346  revert = true;
347  continue;
348  }
349 
350  // This only works for a subset of the possible cases, and doesn't work for
351  // worst(best(a,b),c,d) terrain. Part of the reason that it doesn't work is that
352  // movetype.cpp starts with a default value of either UNREACHABLE or zero, which
353  // when inverted would drown out the values in best(a,b). Another part of the reason
354  // is that the insertion of a plus or minus before the base terrain is commented out
355  // in this function.
356 
357  if(*i == t_translation::BASE) {
358  t_translation::ter_list::iterator insert_it = first.erase(i);
359  //if we are in reverse mode, insert PLUS before and MINUS after the base list
360  //so calculation of base aliases will work normal
361  if(revert) {
362 // insert_it = first.insert(insert_it, t_translation::PLUS);
363 // insert_it++;
364  insert_it = first.insert(insert_it, t_translation::MINUS);
365  }
366  else {
367  //else insert PLUS after the base aliases to restore previous "reverse state"
368  insert_it = first.insert(insert_it, t_translation::PLUS);
369  }
370 
371  first.insert(insert_it, second.begin(), second.end());
372 
373  break;
374  }
375  }
376 
377 }
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
bool empty() const
Definition: tstring.hpp:187
std::string base_str() const
Definition: tstring.hpp:195
bool castle_
Definition: terrain.hpp:235
bool submerge_set_
Definition: terrain.hpp:221
int light_modification_
Definition: terrain.hpp:223
std::string id_
Definition: terrain.hpp:201
t_string income_description_
Definition: terrain.hpp:228
bool overlay_
Definition: terrain.hpp:237
std::string editor_image_
The image used in the editor palette if not defined in WML it will be initialized with the value of m...
Definition: terrain.hpp:200
t_translation::terrain_code editor_default_base_
Definition: terrain.hpp:238
t_translation::terrain_code terrain_with_default_base() const
Definition: terrain.cpp:297
int height_adjust_
Definition: terrain.hpp:217
t_translation::ter_list mvt_type_
Definition: terrain.hpp:212
t_string editor_name_
Definition: terrain.hpp:203
std::string minimap_image_overlay_
Definition: terrain.hpp:194
int max_light_
Definition: terrain.hpp:224
t_translation::terrain_code number_
Definition: terrain.hpp:211
t_string income_description_ally_
Definition: terrain.hpp:229
t_translation::ter_list union_type_
Definition: terrain.hpp:215
bool combined_
Definition: terrain.hpp:237
bool hide_help_
Definition: terrain.hpp:239
t_translation::ter_list def_type_
Definition: terrain.hpp:214
t_string description_
Definition: terrain.hpp:204
const t_string & description() const
Definition: terrain.hpp:50
std::string minimap_image_
The image used in the minimap.
Definition: terrain.hpp:193
terrain_type()
Creates an instance for which is_nonnull() returns false.
Definition: terrain.cpp:36
double submerge_
Definition: terrain.hpp:220
t_string income_description_own_
Definition: terrain.hpp:231
t_translation::ter_list vision_type_
Definition: terrain.hpp:213
int min_light_
Definition: terrain.hpp:225
bool village_
Definition: terrain.hpp:235
bool hide_in_editor_
Definition: terrain.hpp:239
t_string name_
Definition: terrain.hpp:202
bool height_adjust_set_
Definition: terrain.hpp:218
t_string income_description_enemy_
Definition: terrain.hpp:230
bool operator==(const terrain_type &other) const
Returns true if all most of the data matches.
Definition: terrain.cpp:304
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:30
std::size_t i
Definition: function.cpp:968
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
Definition: gettext.hpp:93
Standard logging facilities (interface).
void remove()
Removes a tip.
Definition: tooltip.cpp:111
terrain_code read_terrain_code(std::string_view str, const ter_layer filler)
Reads a single terrain from a string.
const terrain_code VOID_TERRAIN
VOID_TERRAIN is used for shrouded hexes.
const terrain_code MINUS
const ter_layer NO_LAYER
Definition: translation.hpp:40
std::vector< terrain_code > ter_list
Definition: translation.hpp:77
const terrain_code BASE
const terrain_code PLUS
ter_list read_list(std::string_view str, const ter_layer filler)
Reads a list of terrains from a string, when reading the.
std::string write_terrain_code(const terrain_code &tcode)
Writes a single terrain code to a string.
const terrain_code NONE_TERRAIN
Definition: translation.hpp:58
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
void merge_alias_lists(t_translation::ter_list &first, const t_translation::ter_list &second)
Insert second vector into first when the terrain _ref^base is encountered.
Definition: terrain.cpp:334
static lg::log_domain log_config("config")
std::string missing_mandatory_wml_key(const std::string &section, const std::string &key, const std::string &primary_key, const std::string &primary_value)
Returns a standard message for a missing wml key.
#define VALIDATE(cond, message)
The macro to use for the validation of WML.