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