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