The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
terrain.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
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
23static 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 , def_type_(1, t_translation::VOID_TERRAIN)
46 , union_type_(1, t_translation::VOID_TERRAIN)
47 , submerge_(0.0)
48 , height_adjust_(0)
49 , submerge_set_(false)
50 , height_adjust_set_(false)
51 , light_modification_(0)
52 , max_light_(0)
53 , min_light_(0)
54 , heals_(0)
55 , income_description_()
56 , income_description_ally_()
57 , income_description_enemy_()
58 , income_description_own_()
59 , editor_group_()
60 , editor_default_base_(t_translation::VOID_TERRAIN)
61 , village_(false)
62 , castle_(false)
63 , keep_(false)
64 , overlay_(false)
65 , combined_(false)
66 , hide_help_(false)
67 , hide_in_editor_(false)
68 , hide_if_impassable_(false)
69{
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"
77 : "terrain/" + cfg["editor_image"].str() + ".png")
78 , id_(cfg["id"])
79 , name_(cfg["name"].t_str())
80 , editor_name_(cfg["editor_name"].t_str())
81 , description_(cfg["description"].t_str())
82 , help_topic_text_(cfg["help_topic_text"].t_str())
83 , number_(t_translation::read_terrain_code(cfg["string"].str()))
84 , mvt_type_()
85 , def_type_()
86 , union_type_()
87 , submerge_(cfg["submerge"].to_double())
88 , height_adjust_(cfg["unit_height_adjust"].to_int())
89 , submerge_set_(!cfg["submerge"].empty())
90 , height_adjust_set_(!cfg["unit_height_adjust"].empty())
91 , light_modification_(cfg["light"].to_int())
92 , max_light_(cfg["max_light"].to_int(light_modification_))
93 , min_light_(cfg["min_light"].to_int(light_modification_))
94 , heals_(cfg["heals"].to_int())
95 , income_description_()
96 , income_description_ally_()
97 , income_description_enemy_()
98 , income_description_own_()
99 , editor_group_(cfg["editor_group"])
100 , editor_default_base_(t_translation::read_terrain_code(cfg["default_base"].str()))
101 , village_(cfg["gives_income"].to_bool())
102 , castle_(cfg["recruit_onto"].to_bool())
103 , keep_(cfg["recruit_from"].to_bool())
104 , overlay_(number_.base == t_translation::NO_LAYER)
105 , combined_(false)
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",
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
138 const t_translation::ter_list& alias = t_translation::read_list(cfg["aliasof"].str());
139 if(!alias.empty()) {
140 mvt_type_ = alias;
141 def_type_ = alias;
142 }
143
144 const t_translation::ter_list& mvt_alias = t_translation::read_list(cfg["mvt_alias"].str());
145 if(!mvt_alias.empty()) {
146 mvt_type_ = mvt_alias;
147 }
148
149 const t_translation::ter_list& def_alias = t_translation::read_list(cfg["def_alias"].str());
150 if(!def_alias.empty()) {
151 def_type_ = def_alias;
152 }
153
155 union_type_.insert( union_type_.end(), def_type_.begin(), def_type_.end() );
156
157 // remove + and -
160
161 // remove doubles
162 std::sort(union_type_.begin(),union_type_.end());
163 union_type_.erase(std::unique(union_type_.begin(), union_type_.end()), union_type_.end());
164
165 //mouse over message are only shown on villages
166 if(village_) {
167 income_description_ = cfg["income_description"].t_str();
169 income_description_ = _("Village");
170 }
171
172 income_description_ally_ = cfg["income_description_ally"].t_str();
174 income_description_ally_ = _("Allied village");
175 }
176
177 income_description_enemy_ = cfg["income_description_enemy"].t_str();
179 income_description_enemy_ = _("Enemy village");
180 }
181
182 income_description_own_ = cfg["income_description_own"].t_str();
184 income_description_own_ = _("Owned village");
185 }
186 }
187}
188
190 : icon_image_()
191 , minimap_image_(base.minimap_image_)
192 , minimap_image_overlay_(overlay.minimap_image_)
193 , editor_image_(base.editor_image_ + "~BLIT(" + overlay.editor_image_ + ")")
194 , id_(base.id_ + "^" + overlay.id_)
195 , name_(overlay.name_)
196 , editor_name_((base.editor_name_.empty() ? base.name_ : base.editor_name_) + " / " + (overlay.editor_name_.empty() ? overlay.name_ : overlay.editor_name_))
197 , description_(overlay.description())
198 , help_topic_text_()
199 , number_(t_translation::terrain_code(base.number_.base, overlay.number_.overlay))
200 , mvt_type_(overlay.mvt_type_)
201 , def_type_(overlay.def_type_)
202 , union_type_()
203 , submerge_(base.submerge_)
204 , height_adjust_(base.height_adjust_)
205 , submerge_set_(base.submerge_set_)
206 , height_adjust_set_(base.height_adjust_set_)
207 , light_modification_(base.light_modification_ + overlay.light_modification_)
208 , max_light_(std::max(base.max_light_, overlay.max_light_))
209 , min_light_(std::min(base.min_light_, overlay.min_light_))
210 , heals_(std::max<int>(base.heals_, overlay.heals_))
211 , income_description_()
212 , income_description_ally_()
213 , income_description_enemy_()
214 , income_description_own_()
215 , editor_group_()
216 , editor_default_base_()
217 , village_(base.village_ || overlay.village_)
218 , castle_(base.castle_ || overlay.castle_)
219 , keep_(base.keep_ || overlay.keep_)
220 , overlay_(false)
221 , combined_(true)
222 , hide_help_(true)
223 , hide_in_editor_(base.hide_in_editor_ || overlay.hide_in_editor_)
224 , hide_if_impassable_(base.hide_if_impassable_ || overlay.hide_if_impassable_)
225{
226 if(description_.empty()) {
227 description_ = base.description();
228 }
229
230 if(overlay.height_adjust_set_) {
231 height_adjust_set_ = true;
232 height_adjust_ = overlay.height_adjust_;
233 }
234
235 if(overlay.submerge_set_) {
236 submerge_set_ = true;
237 submerge_ = overlay.submerge_;
238 }
239
242
244 union_type_.insert( union_type_.end(), def_type_.begin(), def_type_.end() );
245
246 // remove + and -
249
250 // remove doubles
251 std::sort(union_type_.begin(),union_type_.end());
252 union_type_.erase(std::unique(union_type_.begin(), union_type_.end()), union_type_.end());
253
254 //mouse over message are only shown on villages
255 if(base.village_) {
260 }
261 else if (overlay.village_) {
262 income_description_ = overlay.income_description_;
263 income_description_ally_ = overlay.income_description_ally_;
264 income_description_enemy_ = overlay.income_description_enemy_;
265 income_description_own_ = overlay.income_description_own_;
266 }
267}
268
275
276bool terrain_type::operator==(const terrain_type& other) const {
277 return minimap_image_ == other.minimap_image_
279 && editor_image_ == other.editor_image_
280 && id_ == other.id_
281 && name_.base_str() == other.name_.base_str()
283 && number_ == other.number_
284 && mvt_type_ == other.mvt_type_
285 && def_type_ == other.def_type_
286 && union_type_ == other.union_type_
289 && submerge_ == other.submerge_
290 && submerge_set_ == other.submerge_set_
292 && max_light_ == other.max_light_
293 && min_light_ == other.min_light_
294 && heals_ == other.heals_
295 && village_ == other.village_
296 && castle_ == other.castle_
297 && keep_ == other.keep_
298 && combined_ == other.combined_
299 && overlay_ == other.overlay_
302 && hide_help_ == other.hide_help_;
303}
304
306{
307 // Insert second vector into first when the terrain _ref^base is encountered
308
309 bool revert = (first.front() == t_translation::MINUS ? true : false);
310 t_translation::ter_list::iterator i;
311
312 for(i = first.begin(); i != first.end(); ++i) {
313 if(*i == t_translation::PLUS) {
314 revert = false;
315 continue;
316 } else if(*i == t_translation::MINUS) {
317 revert = true;
318 continue;
319 }
320
321 // This only works for a subset of the possible cases, and doesn't work for
322 // worst(best(a,b),c,d) terrain. Part of the reason that it doesn't work is that
323 // movetype.cpp starts with a default value of either UNREACHABLE or zero, which
324 // when inverted would drown out the values in best(a,b). Another part of the reason
325 // is that the insertion of a plus or minus before the base terrain is commented out
326 // in this function.
327
328 if(*i == t_translation::BASE) {
329 t_translation::ter_list::iterator insert_it = first.erase(i);
330 //if we are in reverse mode, insert PLUS before and MINUS after the base list
331 //so calculation of base aliases will work normal
332 if(revert) {
333// insert_it = first.insert(insert_it, t_translation::PLUS);
334// insert_it++;
335 insert_it = first.insert(insert_it, t_translation::MINUS);
336 }
337 else {
338 //else insert PLUS after the base aliases to restore previous "reverse state"
339 insert_it = first.insert(insert_it, t_translation::PLUS);
340 }
341
342 first.insert(insert_it, second.begin(), second.end());
343
344 break;
345 }
346 }
347
348}
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
bool empty() const
Definition tstring.hpp:200
std::string base_str() const
Definition tstring.hpp:209
bool has_default_base() const
Definition terrain.hpp:187
bool submerge_set_
Definition terrain.hpp:249
int light_modification_
Definition terrain.hpp:252
std::string id_
Definition terrain.hpp:231
t_string income_description_
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:230
t_translation::terrain_code editor_default_base_
Definition terrain.hpp:263
t_translation::terrain_code terrain_with_default_base() const
Return the overlay part of this terrain, on the default_base().
Definition terrain.cpp:269
int height_adjust_
Definition terrain.hpp:247
t_translation::ter_list mvt_type_
Definition terrain.hpp:242
t_string editor_name_
Definition terrain.hpp:233
std::string minimap_image_overlay_
Definition terrain.hpp:224
t_translation::terrain_code number_
Definition terrain.hpp:241
t_string income_description_ally_
Definition terrain.hpp:258
t_translation::ter_list union_type_
Definition terrain.hpp:244
bool hide_help_
Definition terrain.hpp:270
t_translation::ter_list def_type_
Definition terrain.hpp:243
t_string description_
Definition terrain.hpp:234
std::string minimap_image_
The image used in the minimap.
Definition terrain.hpp:223
terrain_type()
Creates an instance for which is_nonnull() returns false.
Definition terrain.cpp:34
double submerge_
Definition terrain.hpp:246
t_string income_description_own_
Definition terrain.hpp:260
bool hide_in_editor_
Definition terrain.hpp:271
t_string name_
Definition terrain.hpp:232
bool height_adjust_set_
Definition terrain.hpp:250
t_string income_description_enemy_
Definition terrain.hpp:259
const t_string & description() const
Definition terrain.hpp:50
bool operator==(const terrain_type &other) const
Returns true if most of the data matches.
Definition terrain.cpp:276
const config * cfg
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
Definition gettext.hpp:100
Standard logging facilities (interface).
STL namespace.
constexpr terrain_code NONE_TERRAIN
const terrain_code MINUS
std::vector< terrain_code > ter_list
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.
std::size_t erase(Container &container, const Value &value)
Convenience wrapper for using std::remove on a container.
Definition general.hpp:119
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
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:305
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.
#define i