The Battle for Wesnoth  1.19.0-dev
attack_type.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 /**
17  * @file
18  * Handle unit-type specific attributes, animations, advancement.
19  */
20 
21 #include "units/attack_type.hpp"
22 #include "formula/callable_objects.hpp"
23 #include "formula/formula.hpp"
24 #include "formula/string_utils.hpp"
26 #include "deprecation.hpp"
27 #include "game_version.hpp"
28 
29 #include "lexical_cast.hpp"
30 #include "log.hpp"
32 #include "gettext.hpp"
33 #include "utils/math.hpp"
34 
35 
36 static lg::log_domain log_config("config");
37 #define ERR_CF LOG_STREAM(err, log_config)
38 #define WRN_CF LOG_STREAM(warn, log_config)
39 #define LOG_CONFIG LOG_STREAM(info, log_config)
40 #define DBG_CF LOG_STREAM(debug, log_config)
41 
42 static lg::log_domain log_unit("unit");
43 #define DBG_UT LOG_STREAM(debug, log_unit)
44 #define ERR_UT LOG_STREAM(err, log_unit)
45 
46 static lg::log_domain log_wml("wml");
47 #define ERR_WML LOG_STREAM(err, log_wml)
48 
50  self_loc_(),
51  other_loc_(),
52  is_attacker_(false),
53  other_attack_(nullptr),
54  description_(cfg["description"].t_str()),
55  id_(cfg["name"]),
56  type_(cfg["type"]),
57  icon_(cfg["icon"]),
58  range_(cfg["range"]),
59  min_range_(cfg["min_range"].to_int(1)),
60  max_range_(cfg["max_range"].to_int(1)),
61  damage_(cfg["damage"]),
62  num_attacks_(cfg["number"]),
63  attack_weight_(cfg["attack_weight"].to_double(1.0)),
64  defense_weight_(cfg["defense_weight"].to_double(1.0)),
65  accuracy_(cfg["accuracy"]),
66  movement_used_(cfg["movement_used"].to_int(100000)),
67  attacks_used_(cfg["attacks_used"].to_int(1)),
68  parry_(cfg["parry"]),
69  specials_(cfg.child_or_empty("specials")),
70  changed_(true)
71 {
72  if (description_.empty())
74 
75  if(icon_.empty()){
76  if (!id_.empty())
77  icon_ = "attacks/" + id_ + ".png";
78  else
79  icon_ = "attacks/blank-attack.png";
80  }
81 }
82 
84 {
85  if(accuracy_ == 0 && parry_ == 0) {
86  return "";
87  }
88 
89  std::ostringstream s;
91 
92  if(parry_ != 0) {
93  s << "/" << utils::signed_percent(parry_);
94  }
95 
96  return s.str();
97 }
98 
99 /**
100  * Returns whether or not *this matches the given @a filter, ignoring the
101  * complexities introduced by [and], [or], and [not].
102  */
103 static bool matches_simple_filter(const attack_type & attack, const config & filter, const std::string& tag_name)
104 {
105  const std::set<std::string> filter_range = utils::split_set(filter["range"].str());
106  const std::string& filter_damage = filter["damage"];
107  const std::string& filter_attacks = filter["number"];
108  const std::string& filter_accuracy = filter["accuracy"];
109  const std::string& filter_parry = filter["parry"];
110  const std::string& filter_movement = filter["movement_used"];
111  const std::string& filter_attacks_used = filter["attacks_used"];
112  const std::set<std::string> filter_name = utils::split_set(filter["name"].str());
113  const std::set<std::string> filter_type = utils::split_set(filter["type"].str());
114  const std::vector<std::string> filter_special = utils::split(filter["special"]);
115  const std::vector<std::string> filter_special_id = utils::split(filter["special_id"]);
116  const std::vector<std::string> filter_special_type = utils::split(filter["special_type"]);
117  const std::vector<std::string> filter_special_active = utils::split(filter["special_active"]);
118  const std::vector<std::string> filter_special_id_active = utils::split(filter["special_id_active"]);
119  const std::vector<std::string> filter_special_type_active = utils::split(filter["special_type_active"]);
120  const std::string filter_formula = filter["formula"];
121 
122  if ( !filter_range.empty() && filter_range.count(attack.range()) == 0 )
123  return false;
124 
125  if ( !filter_damage.empty() && !in_ranges(attack.damage(), utils::parse_ranges_unsigned(filter_damage)) )
126  return false;
127 
128  if (!filter_attacks.empty() && !in_ranges(attack.num_attacks(), utils::parse_ranges_unsigned(filter_attacks)))
129  return false;
130 
131  if (!filter_accuracy.empty() && !in_ranges(attack.accuracy(), utils::parse_ranges_int(filter_accuracy)))
132  return false;
133 
134  if (!filter_parry.empty() && !in_ranges(attack.parry(), utils::parse_ranges_int(filter_parry)))
135  return false;
136 
137  if (!filter_movement.empty() && !in_ranges(attack.movement_used(), utils::parse_ranges_unsigned(filter_movement)))
138  return false;
139 
140  if (!filter_attacks_used.empty() && !in_ranges(attack.attacks_used(), utils::parse_ranges_unsigned(filter_attacks_used)))
141  return false;
142 
143  if ( !filter_name.empty() && filter_name.count(attack.id()) == 0)
144  return false;
145 
146  if (!filter_type.empty()){
147  //if special is type "damage_type" then check attack.type() only for don't have infinite recursion by calling damage_type() below.
148  if(tag_name == "damage_type"){
149  if (filter_type.count(attack.type()) == 0){
150  return false;
151  }
152  } else {
153  //if the type is different from "damage_type" then damage_type() can be called for safe checking.
154  std::pair<std::string, std::string> damage_type = attack.damage_type();
155  if (filter_type.count(damage_type.first) == 0 && filter_type.count(damage_type.second) == 0){
156  return false;
157  }
158  }
159  }
160 
161  if(!filter_special.empty()) {
162  deprecated_message("special=", DEP_LEVEL::PREEMPTIVE, {1, 17, 0}, "Please use special_id or special_type instead");
163  bool found = false;
164  for(auto& special : filter_special) {
165  if(attack.has_special(special, true)) {
166  found = true;
167  break;
168  }
169  }
170  if(!found) {
171  return false;
172  }
173  }
174  if(!filter_special_id.empty()) {
175  bool found = false;
176  for(auto& special : filter_special_id) {
177  if(attack.has_special(special, true, true, false)) {
178  found = true;
179  break;
180  }
181  }
182  if(!found) {
183  return false;
184  }
185  }
186 
187  if(!filter_special_active.empty()) {
188  deprecated_message("special_active=", DEP_LEVEL::PREEMPTIVE, {1, 17, 0}, "Please use special_id_active or special_type_active instead");
189  bool found = false;
190  for(auto& special : filter_special_active) {
191  if(attack.has_special(special, false)) {
192  found = true;
193  break;
194  }
195  }
196  if(!found) {
197  return false;
198  }
199  }
200  if(!filter_special_id_active.empty()) {
201  bool found = false;
202  for(auto& special : filter_special_id_active) {
203  if(attack.has_special_or_ability(special, true, false)) {
204  found = true;
205  break;
206  }
207  }
208  if(!found) {
209  return false;
210  }
211  }
212  if(!filter_special_type.empty()) {
213  bool found = false;
214  for(auto& special : filter_special_type) {
215  if(attack.has_special(special, true, false)) {
216  found = true;
217  break;
218  }
219  }
220  if(!found) {
221  return false;
222  }
223  }
224  if(!filter_special_type_active.empty()) {
225  bool found = false;
226  for(auto& special : filter_special_type_active) {
227  if(attack.has_special_or_ability(special, false)) {
228  found = true;
229  break;
230  }
231  }
232  if(!found) {
233  return false;
234  }
235  }
236 
237  if (!filter_formula.empty()) {
238  try {
239  const wfl::attack_type_callable callable(attack);
240  const wfl::formula form(filter_formula, new wfl::gamestate_function_symbol_table);
241  if(!form.evaluate(callable).as_bool()) {
242  return false;
243  }
244  } catch(const wfl::formula_error& e) {
245  lg::log_to_chat() << "Formula error in weapon filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
246  ERR_WML << "Formula error in weapon filter: " << e.type << " at " << e.filename << ':' << e.line << ")";
247  // Formulae with syntax errors match nothing
248  return false;
249  }
250  }
251 
252  // Passed all tests.
253  return true;
254 }
255 
256 /**
257  * Returns whether or not *this matches the given @a filter.
258  */
259 bool attack_type::matches_filter(const config& filter, const std::string& tag_name) const
260 {
261  // Handle the basic filter.
262  bool matches = matches_simple_filter(*this, filter, tag_name);
263 
264  // Handle [and], [or], and [not] with in-order precedence
265  for (const config::any_child condition : filter.all_children_range() )
266  {
267  // Handle [and]
268  if ( condition.key == "and" )
269  matches = matches && matches_filter(condition.cfg, tag_name);
270 
271  // Handle [or]
272  else if ( condition.key == "or" )
273  matches = matches || matches_filter(condition.cfg, tag_name);
274 
275  // Handle [not]
276  else if ( condition.key == "not" )
277  matches = matches && !matches_filter(condition.cfg, tag_name);
278  }
279 
280  return matches;
281 }
282 
283 /**
284  * Modifies *this using the specifications in @a cfg, but only if *this matches
285  * @a cfg viewed as a filter.
286  *
287  * @returns whether or not @c this matched the @a cfg as a filter.
288  */
290 {
291  if( !matches_filter(cfg) )
292  return false;
293 
294  set_changed(true);
295  const std::string& set_name = cfg["set_name"];
296  const t_string& set_desc = cfg["set_description"];
297  const std::string& set_type = cfg["set_type"];
298  const std::string& set_range = cfg["set_range"];
299  const std::string& set_icon = cfg["set_icon"];
300  const std::string& del_specials = cfg["remove_specials"];
301  auto set_specials = cfg.optional_child("set_specials");
302  const std::string& increase_damage = cfg["increase_damage"];
303  const std::string& set_damage = cfg["set_damage"];
304  const std::string& increase_attacks = cfg["increase_attacks"];
305  const std::string& set_attacks = cfg["set_attacks"];
306  const std::string& set_attack_weight = cfg["attack_weight"];
307  const std::string& set_defense_weight = cfg["defense_weight"];
308  const std::string& increase_accuracy = cfg["increase_accuracy"];
309  const std::string& set_accuracy = cfg["set_accuracy"];
310  const std::string& increase_parry = cfg["increase_parry"];
311  const std::string& set_parry = cfg["set_parry"];
312  const std::string& increase_movement = cfg["increase_movement_used"];
313  const std::string& set_movement = cfg["set_movement_used"];
314  const std::string& increase_attacks_used = cfg["increase_attacks_used"];
315  const std::string& set_attacks_used = cfg["set_attacks_used"];
316  // NB: If you add something here that requires a description,
317  // it needs to be added to describe_modification as well.
318 
319  if(set_name.empty() == false) {
320  id_ = set_name;
321  }
322 
323  if(set_desc.empty() == false) {
324  description_ = set_desc;
325  }
326 
327  if(set_type.empty() == false) {
328  type_ = set_type;
329  }
330 
331  if(set_range.empty() == false) {
332  range_ = set_range;
333  }
334 
335  if(set_icon.empty() == false) {
336  icon_ = set_icon;
337  }
338 
339  if(del_specials.empty() == false) {
340  const std::vector<std::string>& dsl = utils::split(del_specials);
341  config new_specials;
342  for (const config::any_child vp : specials_.all_children_range()) {
343  std::vector<std::string>::const_iterator found_id =
344  std::find(dsl.begin(), dsl.end(), vp.cfg["id"].str());
345  if (found_id == dsl.end()) {
346  new_specials.add_child(vp.key, vp.cfg);
347  }
348  }
349  specials_ = new_specials;
350  }
351 
352  if(set_specials) {
353  const std::string &mode = set_specials["mode"];
354  if(mode.empty()){
355  deprecated_message("[set_specials]mode=<unset>", DEP_LEVEL::INDEFINITE, "",
356  "The mode defaults to 'replace', but should often be 'append' instead. The default may change in a future version, or the attribute may become mandatory.");
357  // fall through to mode != "append"
358  }
359  if(mode != "append") {
360  specials_.clear();
361  }
362  for(const config::any_child value : set_specials->all_children_range()) {
363  specials_.add_child(value.key, value.cfg);
364  }
365  }
366 
367  if(set_damage.empty() == false) {
368  damage_ = std::stoi(set_damage);
369  if (damage_ < 0) {
370  damage_ = 0;
371  }
372  }
373 
374  if(increase_damage.empty() == false) {
375  damage_ = utils::apply_modifier(damage_, increase_damage);
376  if(damage_ < 0) {
377  damage_ = 0;
378  }
379  }
380 
381  if(set_attacks.empty() == false) {
382  num_attacks_ = std::stoi(set_attacks);
383  if (num_attacks_ < 0) {
384  num_attacks_ = 0;
385  }
386 
387  }
388 
389  if(increase_attacks.empty() == false) {
390  num_attacks_ = utils::apply_modifier(num_attacks_, increase_attacks, 1);
391  }
392 
393  if(set_accuracy.empty() == false) {
394  accuracy_ = std::stoi(set_accuracy);
395  }
396 
397  if(increase_accuracy.empty() == false) {
398  accuracy_ = utils::apply_modifier(accuracy_, increase_accuracy);
399  }
400 
401  if(set_parry.empty() == false) {
402  parry_ = std::stoi(set_parry);
403  }
404 
405  if(increase_parry.empty() == false) {
406  parry_ = utils::apply_modifier(parry_, increase_parry);
407  }
408 
409  if(set_movement.empty() == false) {
410  movement_used_ = std::stoi(set_movement);
411  }
412 
413  if(increase_movement.empty() == false) {
414  movement_used_ = utils::apply_modifier(movement_used_, increase_movement, 1);
415  }
416 
417  if(set_attacks_used.empty() == false) {
418  attacks_used_ = std::stoi(set_attacks_used);
419  }
420 
421  if(increase_attacks_used.empty() == false) {
422  attacks_used_ = utils::apply_modifier(attacks_used_, increase_attacks_used, 1);
423  }
424 
425  if(set_attack_weight.empty() == false) {
426  attack_weight_ = lexical_cast_default<double>(set_attack_weight,1.0);
427  }
428 
429  if(set_defense_weight.empty() == false) {
430  defense_weight_ = lexical_cast_default<double>(set_defense_weight,1.0);
431  }
432 
433  return true;
434 }
435 
436 /**
437  * Trimmed down version of apply_modification(), with no modifications actually
438  * made. This can be used to get a description of the modification(s) specified
439  * by @a cfg (if *this matches cfg as a filter).
440  *
441  * If *description is provided, it will be set to a (translated) description
442  * of the modification(s) applied (currently only changes to the number of
443  * strikes, damage, accuracy, and parry are included in this description).
444  *
445  * @returns whether or not @c this matched the @a cfg as a filter.
446  */
447 bool attack_type::describe_modification(const config& cfg,std::string* description)
448 {
449  if( !matches_filter(cfg) )
450  return false;
451 
452  // Did the caller want the description?
453  if(description != nullptr) {
454  const std::string& increase_damage = cfg["increase_damage"];
455  const std::string& set_damage = cfg["set_damage"];
456  const std::string& increase_attacks = cfg["increase_attacks"];
457  const std::string& set_attacks = cfg["set_attacks"];
458  const std::string& increase_accuracy = cfg["increase_accuracy"];
459  const std::string& set_accuracy = cfg["set_accuracy"];
460  const std::string& increase_parry = cfg["increase_parry"];
461  const std::string& set_parry = cfg["set_parry"];
462  const std::string& increase_movement = cfg["increase_movement_used"];
463  const std::string& set_movement = cfg["set_movement_used"];
464  const std::string& increase_attacks_used = cfg["increase_attacks_used"];
465  const std::string& set_attacks_used = cfg["set_attacks_used"];
466 
467  std::vector<t_string> desc;
468 
469  if(!increase_damage.empty()) {
470  desc.emplace_back(VNGETTEXT(
471  // TRANSLATORS: Current value for WML code increase_damage, documented in https://wiki.wesnoth.org/EffectWML
472  "<span color=\"$color\">$number_or_percent</span> damage",
473  "<span color=\"$color\">$number_or_percent</span> damage",
474  std::stoi(increase_damage),
475  {{"number_or_percent", utils::print_modifier(increase_damage)}, {"color", increase_damage[0] == '-' ? "#f00" : "#0f0"}}));
476  }
477 
478  if(!set_damage.empty()) {
479  // TRANSLATORS: Current value for WML code set_damage, documented in https://wiki.wesnoth.org/EffectWML
480  desc.emplace_back(VNGETTEXT(
481  "$number damage",
482  "$number damage",
483  std::stoi(set_damage),
484  {{"number", set_damage}}));
485  }
486 
487  if(!increase_attacks.empty()) {
488  desc.emplace_back(VNGETTEXT(
489  // TRANSLATORS: Current value for WML code increase_attacks, documented in https://wiki.wesnoth.org/EffectWML
490  "<span color=\"$color\">$number_or_percent</span> strike",
491  "<span color=\"$color\">$number_or_percent</span> strikes",
492  std::stoi(increase_attacks),
493  {{"number_or_percent", utils::print_modifier(increase_attacks)}, {"color", increase_attacks[0] == '-' ? "#f00" : "#0f0"}}));
494  }
495 
496  if(!set_attacks.empty()) {
497  desc.emplace_back(VNGETTEXT(
498  // TRANSLATORS: Current value for WML code set_attacks, documented in https://wiki.wesnoth.org/EffectWML
499  "$number strike",
500  "$number strikes",
501  std::stoi(set_attacks),
502  {{"number", set_attacks}}));
503  }
504 
505  if(!set_accuracy.empty()) {
506  desc.emplace_back(VGETTEXT(
507  // TRANSLATORS: Current value for WML code set_accuracy, documented in https://wiki.wesnoth.org/EffectWML
508  "$number| accuracy",
509  {{"number", set_accuracy}}));
510  }
511 
512  if(!increase_accuracy.empty()) {
513  desc.emplace_back(VGETTEXT(
514  // TRANSLATORS: Current value for WML code increase_accuracy, documented in https://wiki.wesnoth.org/EffectWML
515  "<span color=\"$color\">$number_or_percent|%</span> accuracy",
516  {{"number_or_percent", utils::print_modifier(increase_accuracy)}, {"color", increase_accuracy[0] == '-' ? "#f00" : "#0f0"}}));
517  }
518 
519  if(!set_parry.empty()) {
520  desc.emplace_back(VGETTEXT(
521  // TRANSLATORS: Current value for WML code set_parry, documented in https://wiki.wesnoth.org/EffectWML
522  "$number parry",
523  {{"number", set_parry}}));
524  }
525 
526  if(!increase_parry.empty()) {
527  desc.emplace_back(VGETTEXT(
528  // TRANSLATORS: Current value for WML code increase_parry, documented in https://wiki.wesnoth.org/EffectWML
529  "<span color=\"$color\">$number_or_percent</span> parry",
530  {{"number_or_percent", utils::print_modifier(increase_parry)}, {"color", increase_parry[0] == '-' ? "#f00" : "#0f0"}}));
531  }
532 
533  if(!set_movement.empty()) {
534  desc.emplace_back(VNGETTEXT(
535  // TRANSLATORS: Current value for WML code set_movement_used, documented in https://wiki.wesnoth.org/EffectWML
536  "$number movement point",
537  "$number movement points",
538  std::stoi(set_movement),
539  {{"number", set_movement}}));
540  }
541 
542  if(!increase_movement.empty()) {
543  desc.emplace_back(VNGETTEXT(
544  // TRANSLATORS: Current value for WML code increase_movement_used, documented in https://wiki.wesnoth.org/EffectWML
545  "<span color=\"$color\">$number_or_percent</span> movement point",
546  "<span color=\"$color\">$number_or_percent</span> movement points",
547  std::stoi(increase_movement),
548  {{"number_or_percent", utils::print_modifier(increase_movement)}, {"color", increase_movement[0] == '-' ? "#f00" : "#0f0"}}));
549  }
550 
551  if(!set_attacks_used.empty()) {
552  desc.emplace_back(VNGETTEXT(
553  // TRANSLATORS: Current value for WML code set_attacks_used, documented in https://wiki.wesnoth.org/EffectWML
554  "$number attack used",
555  "$number attacks used",
556  std::stoi(set_attacks_used),
557  {{"number", set_attacks_used}}));
558  }
559 
560  if(!increase_attacks_used.empty()) {
561  desc.emplace_back(VNGETTEXT(
562  // TRANSLATORS: Current value for WML code increase_attacks_used, documented in https://wiki.wesnoth.org/EffectWML
563  "<span color=\"$color\">$number_or_percent</span> attack used",
564  "<span color=\"$color\">$number_or_percent</span> attacks used",
565  std::stoi(increase_attacks_used),
566  {{"number_or_percent", utils::print_modifier(increase_attacks_used)}, {"color", increase_attacks_used[0] == '-' ? "#f00" : "#0f0"}}));
567  }
568 
569  *description = utils::format_conjunct_list("", desc);
570  }
571 
572  return true;
573 }
574 
575 void attack_type::write(config& cfg) const
576 {
577  cfg["description"] = description_;
578  cfg["name"] = id_;
579  cfg["type"] = type_;
580  cfg["icon"] = icon_;
581  cfg["range"] = range_;
582  cfg["min_range"] = min_range_;
583  cfg["max_range"] = max_range_;
584  cfg["damage"] = damage_;
585  cfg["number"] = num_attacks_;
586  cfg["attack_weight"] = attack_weight_;
587  cfg["defense_weight"] = defense_weight_;
588  cfg["accuracy"] = accuracy_;
589  cfg["movement_used"] = movement_used_;
590  cfg["attacks_used"] = attacks_used_;
591  cfg["parry"] = parry_;
592  cfg.add_child("specials", specials_);
593 }
static lg::log_domain log_unit("unit")
#define ERR_WML
Definition: attack_type.cpp:47
static lg::log_domain log_wml("wml")
static bool matches_simple_filter(const attack_type &attack, const config &filter, const std::string &tag_name)
Returns whether or not *this matches the given filter, ignoring the complexities introduced by [and],...
static lg::log_domain log_config("config")
bool has_special(const std::string &special, bool simple_check=false, bool special_id=true, bool special_tags=true) const
Returns whether or not *this has a special with a tag or id equal to special.
Definition: abilities.cpp:752
const std::string & range() const
Definition: attack_type.hpp:45
void set_attacks_used(int value)
int movement_used() const
void set_accuracy(int value)
Definition: attack_type.hpp:62
const std::string & type() const
Definition: attack_type.hpp:43
int parry() const
Definition: attack_type.hpp:50
std::string accuracy_parry_description() const
Definition: attack_type.cpp:83
bool apply_modification(const config &cfg)
Modifies *this using the specifications in cfg, but only if *this matches cfg viewed as a filter.
void set_specials(config value)
Definition: attack_type.hpp:68
config specials_
void set_defense_weight(double value)
Definition: attack_type.hpp:67
int num_attacks() const
Definition: attack_type.hpp:52
void set_changed(bool value)
std::string type_
std::string icon_
void set_parry(int value)
Definition: attack_type.hpp:63
void set_attack_weight(double value)
Definition: attack_type.hpp:66
void set_damage(int value)
Definition: attack_type.hpp:64
bool describe_modification(const config &cfg, std::string *description)
Trimmed down version of apply_modification(), with no modifications actually made.
int attacks_used() const
const std::string & id() const
Definition: attack_type.hpp:42
void set_icon(const std::string &value)
Definition: attack_type.hpp:60
double defense_weight_
std::string id_
double attack_weight_
std::string range_
attack_type(const config &cfg)
Definition: attack_type.cpp:49
void set_type(const std::string &value)
Definition: attack_type.hpp:59
void write(config &cfg) const
bool matches_filter(const config &filter, const std::string &tag_name="") const
Returns whether or not *this matches the given filter.
int accuracy() const
Definition: attack_type.hpp:49
void set_range(const std::string &value)
Definition: attack_type.hpp:61
int damage() const
Definition: attack_type.hpp:51
bool has_special_or_ability(const std::string &special, bool special_id=true, bool special_tags=true) const
used for abilities used like weapon and true specials
Definition: abilities.cpp:1668
t_string description_
void set_name(const t_string &value)
Definition: attack_type.hpp:57
std::pair< std::string, std::string > damage_type() const
return a modified damage type and/or add a secondary_type for hybrid use if special is active.
Definition: abilities.cpp:1198
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:887
void clear()
Definition: config.cpp:831
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:385
config & add_child(config_key_type key)
Definition: config.cpp:441
bool empty() const
Definition: tstring.hpp:186
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:40
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:313
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:29
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
#define VNGETTEXT(msgid, msgid_plural, count,...)
Interfaces for manipulating version numbers of engine, add-ons, etc.
New lexcical_cast header.
Standard logging facilities (interface).
General math utility functions.
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp >> &ranges)
Definition: math.hpp:87
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:544
std::string egettext(char const *msgid)
Definition: gettext.cpp:429
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
std::vector< std::pair< int, int > > parse_ranges_int(const std::string &str)
Handles a comma-separated list of inputs to parse_range.
std::vector< std::pair< int, int > > parse_ranges_unsigned(const std::string &str)
Handles a comma-separated list of inputs to parse_range, in a context that does not expect negative v...
int apply_modifier(const int number, const std::string &amount, const int minimum)
std::string format_conjunct_list(const t_string &empty, const std::vector< t_string > &elems)
Format a conjunctive list.
std::string signed_percent(int val)
Convert into a percentage (using the Unicode "−" and +0% convention.
std::vector< std::string > split(const config_attribute_value &val)
std::string print_modifier(const std::string &mod)
Add a "+" or replace the "-" par Unicode minus.
static map_location::DIRECTION s
#define e