The Battle for Wesnoth  1.19.5+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 #include "utils/general.hpp"
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 }
72 
74  : icon_image_(cfg["icon_image"])
75  , minimap_image_(cfg["symbol_image"])
76  , minimap_image_overlay_()
77  , editor_image_(cfg["editor_image"].empty() ? "terrain/" + minimap_image_ + ".png"
78  : "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"].to_int())
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"].to_int())
94  , max_light_(cfg["max_light"].to_int(light_modification_))
95  , min_light_(cfg["min_light"].to_int(light_modification_))
96  , heals_(cfg["heals"].to_int())
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 -
174 
175  // remove doubles
176  std::sort(union_type_.begin(),union_type_.end());
177  union_type_.erase(std::unique(union_type_.begin(), union_type_.end()), union_type_.end());
178 
179 
180 
181  //mouse over message are only shown on villages
182  if(village_) {
183  income_description_ = cfg["income_description"];
184  if(income_description_.empty()) {
185  income_description_ = _("Village");
186  }
187 
188  income_description_ally_ = cfg["income_description_ally"];
190  income_description_ally_ = _("Allied village");
191  }
192 
193  income_description_enemy_ = cfg["income_description_enemy"];
195  income_description_enemy_ = _("Enemy village");
196  }
197 
198  income_description_own_ = cfg["income_description_own"];
200  income_description_own_ = _("Owned village");
201  }
202  }
203 }
204 
206  icon_image_(),
207  minimap_image_(base.minimap_image_),
208  minimap_image_overlay_(overlay.minimap_image_),
209  editor_image_(base.editor_image_ + "~BLIT(" + overlay.editor_image_ +")"),
210  id_(base.id_+"^"+overlay.id_),
211  name_(overlay.name_),
212  editor_name_((base.editor_name_.empty() ? base.name_ : base.editor_name_) + " / " + (overlay.editor_name_.empty() ? overlay.name_ : overlay.editor_name_)),
213  description_(overlay.description()),
214  help_topic_text_(),
215  number_(t_translation::terrain_code(base.number_.base, overlay.number_.overlay)),
216  mvt_type_(overlay.mvt_type_),
217  vision_type_(overlay.vision_type_),
218  def_type_(overlay.def_type_),
219  union_type_(),
220  height_adjust_(base.height_adjust_),
221  height_adjust_set_(base.height_adjust_set_),
222  submerge_(base.submerge_),
223  submerge_set_(base.submerge_set_),
224  light_modification_(base.light_modification_ + overlay.light_modification_),
225  max_light_(std::max(base.max_light_, overlay.max_light_)),
226  min_light_(std::min(base.min_light_, overlay.min_light_)),
227  heals_(std::max<int>(base.heals_, overlay.heals_)),
228  income_description_(),
229  income_description_ally_(),
230  income_description_enemy_(),
231  income_description_own_(),
232  editor_group_(),
233  village_(base.village_ || overlay.village_),
234  castle_(base.castle_ || overlay.castle_),
235  keep_(base.keep_ || overlay.keep_),
236  overlay_(false),
237  combined_(true),
238  editor_default_base_(),
239  hide_help_(true),
240  hide_in_editor_(base.hide_in_editor_ || overlay.hide_in_editor_),
241  hide_if_impassable_(base.hide_if_impassable_ || overlay.hide_if_impassable_)
242 {
243  if(description_.empty()) {
244  description_ = base.description();
245  }
246 
247  if(overlay.height_adjust_set_) {
248  height_adjust_set_ = true;
249  height_adjust_ = overlay.height_adjust_;
250  }
251 
252  if(overlay.submerge_set_) {
253  submerge_set_ = true;
254  submerge_ = overlay.submerge_;
255  }
256 
260 
262  union_type_.insert( union_type_.end(), def_type_.begin(), def_type_.end() );
263  union_type_.insert( union_type_.end(), vision_type_.begin(), vision_type_.end() );
264 
265  // remove + and -
268 
269  // remove doubles
270  std::sort(union_type_.begin(),union_type_.end());
271  union_type_.erase(std::unique(union_type_.begin(), union_type_.end()), union_type_.end());
272 
273 
274 
275  //mouse over message are only shown on villages
276  if(base.village_) {
281  }
282  else if (overlay.village_) {
283  income_description_ = overlay.income_description_;
284  income_description_ally_ = overlay.income_description_ally_;
285  income_description_enemy_ = overlay.income_description_enemy_;
286  income_description_own_ = overlay.income_description_own_;
287  }
288 
289 }
290 
292  if(overlay_ && has_default_base()) {
294  }
295  return number_;
296 }
297 
298 bool terrain_type::operator==(const terrain_type& other) const {
299  return minimap_image_ == other.minimap_image_
301  && editor_image_ == other.editor_image_
302  && id_ == other.id_
303  && name_.base_str() == other.name_.base_str()
305  && number_ == other.number_
306  && mvt_type_ == other.mvt_type_
307  && vision_type_ == other.vision_type_
308  && def_type_ == other.def_type_
309  && union_type_ == other.union_type_
310  && height_adjust_ == other.height_adjust_
312  && submerge_ == other.submerge_
313  && submerge_set_ == other.submerge_set_
315  && max_light_ == other.max_light_
316  && min_light_ == other.min_light_
317  && heals_ == other.heals_
318  && village_ == other.village_
319  && castle_ == other.castle_
320  && keep_ == other.keep_
321  && combined_ == other.combined_
322  && overlay_ == other.overlay_
324  && hide_in_editor_ == other.hide_in_editor_
325  && hide_help_ == other.hide_help_;
326 }
327 
329 {
330  // Insert second vector into first when the terrain _ref^base is encountered
331 
332  bool revert = (first.front() == t_translation::MINUS ? true : false);
334 
335  for(i = first.begin(); i != first.end(); ++i) {
336  if(*i == t_translation::PLUS) {
337  revert = false;
338  continue;
339  } else if(*i == t_translation::MINUS) {
340  revert = true;
341  continue;
342  }
343 
344  // This only works for a subset of the possible cases, and doesn't work for
345  // worst(best(a,b),c,d) terrain. Part of the reason that it doesn't work is that
346  // movetype.cpp starts with a default value of either UNREACHABLE or zero, which
347  // when inverted would drown out the values in best(a,b). Another part of the reason
348  // is that the insertion of a plus or minus before the base terrain is commented out
349  // in this function.
350 
351  if(*i == t_translation::BASE) {
352  t_translation::ter_list::iterator insert_it = first.erase(i);
353  //if we are in reverse mode, insert PLUS before and MINUS after the base list
354  //so calculation of base aliases will work normal
355  if(revert) {
356 // insert_it = first.insert(insert_it, t_translation::PLUS);
357 // insert_it++;
358  insert_it = first.insert(insert_it, t_translation::MINUS);
359  }
360  else {
361  //else insert PLUS after the base aliases to restore previous "reverse state"
362  insert_it = first.insert(insert_it, t_translation::PLUS);
363  }
364 
365  first.insert(insert_it, second.begin(), second.end());
366 
367  break;
368  }
369  }
370 
371 }
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
bool empty() const
Definition: tstring.hpp:194
std::string base_str() const
Definition: tstring.hpp:202
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:291
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:298
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:1028
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
Definition: gettext.hpp:93
Standard logging facilities (interface).
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::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
Definition: general.hpp:111
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:328
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.