The Battle for Wesnoth  1.19.2+dev
abilities.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2024
3  by Dominic Bolin <dominic.bolin@exong.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  * Manage unit-abilities, like heal, cure, and weapon_specials.
19  */
20 
21 #include "display.hpp"
22 #include "display_context.hpp"
23 #include "font/text_formatting.hpp"
24 #include "game_board.hpp"
25 #include "game_version.hpp" // for version_info
26 #include "gettext.hpp"
27 #include "global.hpp"
28 #include "lexical_cast.hpp"
29 #include "log.hpp"
30 #include "map/map.hpp"
31 #include "resources.hpp"
32 #include "team.hpp"
33 #include "terrain/filter.hpp"
34 #include "units/unit.hpp"
35 #include "units/abilities.hpp"
36 #include "units/filter.hpp"
37 #include "units/map.hpp"
38 #include "filter_context.hpp"
39 #include "formula/callable_objects.hpp"
40 #include "formula/formula.hpp"
42 #include "deprecation.hpp"
43 
44 
45 
46 static lg::log_domain log_engine("engine");
47 #define ERR_NG LOG_STREAM(err, log_engine)
48 
49 static lg::log_domain log_wml("wml");
50 #define ERR_WML LOG_STREAM(err, log_wml)
51 
52 namespace {
53  class temporary_facing
54  {
55  map_location::DIRECTION save_dir_;
56  unit_const_ptr u_;
57  public:
58  temporary_facing(unit_const_ptr u, map_location::DIRECTION new_dir)
59  : save_dir_(u ? u->facing() : map_location::NDIRECTIONS)
60  , u_(u)
61  {
62  if (u_) {
63  u_->set_facing(new_dir);
64  }
65  }
66  ~temporary_facing()
67  {
68  if (u_) {
69  u_->set_facing(save_dir_);
70  }
71  }
72  };
73 }
74 
75 /*
76  *
77  * [abilities]
78  * ...
79  *
80  * [heals]
81  * value=4
82  * max_value=8
83  * cumulative=no
84  * affect_allies=yes
85  * name= _ "heals"
86  * female_name= _ "female^heals"
87  * name_inactive=null
88  * female_name_inactive=null
89  * description= _ "Heals:
90 Allows the unit to heal adjacent friendly units at the beginning of each turn.
91 
92 A unit cared for by a healer may heal up to 4 HP per turn.
93 A poisoned unit cannot be cured of its poison by a healer, and must seek the care of a village or a unit that can cure."
94  * description_inactive=null
95  *
96  * affect_self=yes
97  * [filter] // SUF
98  * ...
99  * [/filter]
100  * [filter_self] // SUF
101  * ...
102  * [/filter_self]
103  * [filter_adjacent] // SUF
104  * adjacent=n,ne,nw
105  * ...
106  * [/filter_adjacent]
107  * [filter_adjacent_location]
108  * adjacent=n,ne,nw
109  * ...
110  * [/filter_adjacent]
111  * [affect_adjacent]
112  * adjacent=n,ne,nw
113  * [filter] // SUF
114  * ...
115  * [/filter]
116  * [/affect_adjacent]
117  * [affect_adjacent]
118  * adjacent=s,se,sw
119  * [filter] // SUF
120  * ...
121  * [/filter]
122  * [/affect_adjacent]
123  *
124  * [/heals]
125  *
126  * ...
127  * [/abilities]
128  *
129  */
130 
131 
132 namespace {
133 
134 const unit_map& get_unit_map()
135 {
136  // Used if we're in the game, including during the construction of the display_context
138  return resources::gameboard->units();
139  }
140 
141  // If we get here, we're in the scenario editor
142  assert(display::get_singleton());
143  return display::get_singleton()->get_units();
144 }
145 
146 const team& get_team(std::size_t side)
147 {
148  // Used if we're in the game, including during the construction of the display_context
150  return resources::gameboard->get_team(side);
151  }
152 
153  // If we get here, we're in the scenario editor
154  assert(display::get_singleton());
156 }
157 
158 /**
159  * Common code for the question "some other unit has an ability, can that ability affect this
160  * unit" - it's not the full answer to that question, just a part of it.
161  *
162  * Although this is called while checking which units' "hides" abilities are active, that's only
163  * for the question "is this unit next to an ally that has a 'camoflages adjacent allies' ability";
164  * not the question "is this unit next to an enemy, therefore visible".
165  */
166 bool affects_side(const config& cfg, std::size_t side, std::size_t other_side)
167 {
168  const team& side_team = get_team(side);
169 
170  if(side == other_side)
171  return cfg["affect_allies"].to_bool(true);
172  if(side_team.is_enemy(other_side))
173  return cfg["affect_enemies"].to_bool();
174  else
175  return cfg["affect_allies"].to_bool();
176 }
177 
178 }
179 
180 bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc) const
181 {
182  for (const config &i : this->abilities_.child_range(tag_name)) {
183  if (ability_active(tag_name, i, loc) &&
184  ability_affects_self(tag_name, i, loc))
185  {
186  return true;
187  }
188  }
189 
190  const unit_map& units = get_unit_map();
191 
192  const auto adjacent = get_adjacent_tiles(loc);
193  for(unsigned i = 0; i < adjacent.size(); ++i) {
194  const unit_map::const_iterator it = units.find(adjacent[i]);
195  if (it == units.end() || it->incapacitated())
196  continue;
197  // Abilities may be tested at locations other than the unit's current
198  // location. This is intentional to allow for less messing with the unit
199  // map during calculations, particularly with regards to movement.
200  // Thus, we need to make sure the adjacent unit (*it) is not actually
201  // ourself.
202  if ( &*it == this )
203  continue;
204  for (const config &j : it->abilities_.child_range(tag_name)) {
205  if (affects_side(j, side(), it->side()) &&
206  it->ability_active(tag_name, j, adjacent[i]) &&
207  ability_affects_adjacent(tag_name, j, i, loc, *it))
208  {
209  return true;
210  }
211  }
212  }
213 
214 
215  return false;
216 }
217 
218 unit_ability_list unit::get_abilities(const std::string& tag_name, const map_location& loc) const
219 {
220  unit_ability_list res(loc_);
221 
222  for(const config& i : this->abilities_.child_range(tag_name)) {
223  if(ability_active(tag_name, i, loc)
224  && ability_affects_self(tag_name, i, loc))
225  {
226  res.emplace_back(&i, loc, loc);
227  }
228  }
229 
230  const unit_map& units = get_unit_map();
231 
232  const auto adjacent = get_adjacent_tiles(loc);
233  for(unsigned i = 0; i < adjacent.size(); ++i) {
234  const unit_map::const_iterator it = units.find(adjacent[i]);
235  if (it == units.end() || it->incapacitated())
236  continue;
237  // Abilities may be tested at locations other than the unit's current
238  // location. This is intentional to allow for less messing with the unit
239  // map during calculations, particularly with regards to movement.
240  // Thus, we need to make sure the adjacent unit (*it) is not actually
241  // ourself.
242  if ( &*it == this )
243  continue;
244  for(const config& j : it->abilities_.child_range(tag_name)) {
245  if(affects_side(j, side(), it->side())
246  && it->ability_active(tag_name, j, adjacent[i])
247  && ability_affects_adjacent(tag_name, j, i, loc, *it))
248  {
249  res.emplace_back(&j, loc, adjacent[i]);
250  }
251  }
252  }
253 
254 
255  return res;
256 }
257 
258 unit_ability_list unit::get_abilities_weapons(const std::string& tag_name, const map_location& loc, const_attack_ptr weapon, const_attack_ptr opp_weapon) const
259 {
260  unit_ability_list res = get_abilities(tag_name, loc);
261  utils::erase_if(res, [&](const unit_ability& i) {
262  return !ability_affects_weapon(*i.ability_cfg, weapon, false) || !ability_affects_weapon(*i.ability_cfg, opp_weapon, true);
263  });
264  return res;
265 }
266 
267 std::vector<std::string> unit::get_ability_list() const
268 {
269  std::vector<std::string> res;
270 
271  for (const config::any_child ab : this->abilities_.all_children_range()) {
272  std::string id = ab.cfg["id"];
273  if (!id.empty())
274  res.push_back(std::move(id));
275  }
276  return res;
277 }
278 
279 
280 namespace {
281  /**
282  * Adds a quadruple consisting of (in order) id, base name,
283  * male or female name as appropriate for the unit, and description.
284  *
285  * @returns Whether name was resolved and quadruple added.
286  */
287  bool add_ability_tooltip(const config::any_child &ab, unit_race::GENDER gender, std::vector<std::tuple<std::string, t_string,t_string,t_string>>& res, bool active)
288  {
289  if (active) {
290  const t_string& name = gender_value(ab.cfg, gender, "name", "female_name", "name").t_str();
291 
292  if (!name.empty()) {
293  res.emplace_back(
294  ab.cfg["id"],
295  ab.cfg["name"].t_str(),
296  name,
297  ab.cfg["description"].t_str() );
298  return true;
299  }
300  }
301  else
302  {
303  // See if an inactive name was specified.
304  const config::attribute_value& inactive_value =
305  gender_value(ab.cfg, gender, "name_inactive",
306  "female_name_inactive", "name_inactive");
307  const t_string& name = !inactive_value.blank() ? inactive_value.t_str() :
308  gender_value(ab.cfg, gender, "name", "female_name", "name").t_str();
309 
310  if (!name.empty()) {
311  res.emplace_back(
312  ab.cfg["id"],
313  ab.cfg.get_or("name_inactive", "name").t_str(),
314  name,
315  ab.cfg.get_or("description_inactive", "description").t_str() );
316  return true;
317  }
318  }
319 
320  return false;
321  }
322 }
323 
324 std::vector<std::tuple<std::string, t_string, t_string, t_string>> unit::ability_tooltips() const
325 {
326  std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
327 
328  for (const config::any_child ab : this->abilities_.all_children_range())
329  {
330  add_ability_tooltip(ab, gender_, res, true);
331  }
332 
333  return res;
334 }
335 
336 std::vector<std::tuple<std::string, t_string, t_string, t_string>> unit::ability_tooltips(boost::dynamic_bitset<>& active_list, const map_location& loc) const
337 {
338  std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
339  active_list.clear();
340 
341  for (const config::any_child ab : this->abilities_.all_children_range())
342  {
343  bool active = ability_active(ab.key, ab.cfg, loc);
344  if (add_ability_tooltip(ab, gender_, res, active))
345  {
346  active_list.push_back(active);
347  }
348  }
349  return res;
350 }
351 
352 bool unit::ability_active(const std::string& ability,const config& cfg,const map_location& loc) const
353 {
354  bool illuminates = ability == "illuminates";
355 
356  if (auto afilter = cfg.optional_child("filter"))
357  if ( !unit_filter(vconfig(*afilter)).set_use_flat_tod(illuminates).matches(*this, loc) )
358  return false;
359 
360  const auto adjacent = get_adjacent_tiles(loc);
361 
362  const unit_map& units = get_unit_map();
363 
364  for (const config &i : cfg.child_range("filter_adjacent"))
365  {
366  std::size_t count = 0;
367  unit_filter ufilt{ vconfig(i) };
368  ufilt.set_use_flat_tod(illuminates);
369  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
370  for (const map_location::DIRECTION index : dirs)
371  {
373  continue;
374  unit_map::const_iterator unit = units.find(adjacent[index]);
375  if (unit == units.end())
376  return false;
377  if (!ufilt(*unit, *this))
378  return false;
379  if((*this).id() == (*unit).id())
380  return false;
381  if (i.has_attribute("is_enemy")) {
383  if (i["is_enemy"].to_bool() != dc.get_team(unit->side()).is_enemy(side_)) {
384  continue;
385  }
386  }
387  count++;
388  }
389  if (i["count"].empty() && count != dirs.size()) {
390  return false;
391  }
392  if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) {
393  return false;
394  }
395  }
396 
397  for (const config &i : cfg.child_range("filter_adjacent_location"))
398  {
399  std::size_t count = 0;
400  terrain_filter adj_filter(vconfig(i), resources::filter_con, false);
401  adj_filter.flatten(illuminates);
402 
403  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
404  for (const map_location::DIRECTION index : dirs)
405  {
407  continue;
408  }
409  if(!adj_filter.match(adjacent[index])) {
410  return false;
411  }
412  count++;
413  }
414  if (i["count"].empty() && count != dirs.size()) {
415  return false;
416  }
417  if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) {
418  return false;
419  }
420  }
421  return true;
422 }
423 
424 bool unit::ability_affects_adjacent(const std::string& ability, const config& cfg,int dir,const map_location& loc,const unit& from) const
425 {
426  bool illuminates = ability == "illuminates";
427 
428  assert(dir >=0 && dir <= 5);
429  map_location::DIRECTION direction = static_cast<map_location::DIRECTION>(dir);
430 
431  for (const config &i : cfg.child_range("affect_adjacent"))
432  {
433  if (i.has_attribute("adjacent")) { //key adjacent defined
434  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
435  if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
436  continue;
437  }
438  }
439  if((*this).id() == from.id()){
440  return false;
441  }
442  auto filter = i.optional_child("filter");
443  if (!filter || //filter tag given
444  unit_filter(vconfig(*filter)).set_use_flat_tod(illuminates).matches(*this, loc, from) ) {
445  return true;
446  }
447  }
448  return false;
449 }
450 
451 bool unit::ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const
452 {
453  auto filter = cfg.optional_child("filter_self");
454  bool affect_self = cfg["affect_self"].to_bool(true);
455  if (!filter || !affect_self) return affect_self;
456  return unit_filter(vconfig(*filter)).set_use_flat_tod(ability == "illuminates").matches(*this, loc);
457 }
458 
459 bool unit::ability_affects_weapon(const config& cfg, const_attack_ptr weapon, bool is_opp) const
460 {
461  const std::string filter_tag_name = is_opp ? "filter_second_weapon" : "filter_weapon";
462  if(!cfg.has_child(filter_tag_name)) {
463  return true;
464  }
465  const config& filter = cfg.mandatory_child(filter_tag_name);
466  if(!weapon) {
467  return false;
468  }
469  return weapon->matches_filter(filter);
470 }
471 
472 bool unit::has_ability_type(const std::string& ability) const
473 {
474  return !abilities_.child_range(ability).empty();
475 }
476 
477 //these two functions below are used in order to add to the unit
478 //a second set of halo encoded in the abilities (like illuminates halo in [illuminates] ability for example)
479 static void add_string_to_vector(std::vector<std::string>& image_list, const config& cfg, const std::string& attribute_name)
480 {
481  auto ret = std::find(image_list.begin(), image_list.end(), cfg[attribute_name].str());
482  if(ret == image_list.end()){
483  image_list.push_back(cfg[attribute_name].str());
484  }
485 }
486 
487 const std::vector<std::string> unit::halo_or_icon_abilities(const std::string& image_type) const
488 {
489  std::vector<std::string> image_list;
491  bool is_active = ability_active(sp.key, sp.cfg, loc_);
492  //Add halo/overlay to owner of ability if active and affect_self is true.
493  if( !(sp.cfg)[image_type + "_image"].str().empty() && is_active && ability_affects_self(sp.key, sp.cfg, loc_)){
494  add_string_to_vector(image_list, sp.cfg,image_type + "_image");
495  }
496  //Add halo/overlay to owner of ability who affect adjacent only if active.
497  if(!(sp.cfg)[image_type + "_image_self"].str().empty() && is_active){
498  add_string_to_vector(image_list, sp.cfg, image_type + "_image_self");
499  }
500  }
501 
502  const unit_map& units = get_unit_map();
503 
504  //Add halo/overlay to unit under abilities owned by adjacent who has [affect_adjacent]
505  //if condition matched
506  const auto adjacent = get_adjacent_tiles(loc_);
507  for(unsigned i = 0; i < adjacent.size(); ++i) {
508  const unit_map::const_iterator it = units.find(adjacent[i]);
509  if (it == units.end() || it->incapacitated())
510  continue;
511  if ( &*it == this )
512  continue;
513  for(const config::any_child j : it->abilities_.all_children_range()) {
514  if(!(j.cfg)[image_type + "_image"].str().empty() && affects_side(j.cfg, side(), it->side()) && it->ability_active(j.key, j.cfg, adjacent[i]) && ability_affects_adjacent(j.key, j.cfg, i, loc_, *it))
515  {
516  add_string_to_vector(image_list, j.cfg, image_type + "_image");
517  }
518  }
519  }
520  //rearranges vector alphabetically when its size equals or exceeds two.
521  if(image_list.size() >= 2){
522  std::sort(image_list.begin(), image_list.end());
523  }
524  return image_list;
525 }
526 
528 {
529  if(unit_const_ptr & att = is_attacker_ ? self_ : other_) {
530  callable.add("attacker", wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
531  }
532  if(unit_const_ptr & def = is_attacker_ ? other_ : self_) {
533  callable.add("defender", wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
534  }
535 }
536 
537 namespace {
538 
539 
540 template<typename T, typename TFuncFormula>
541 class get_ability_value_visitor
542 #ifdef USING_BOOST_VARIANT
543  : public boost::static_visitor<T>
544 #endif
545 {
546 public:
547  // Constructor stores the default value.
548  get_ability_value_visitor(T def, const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
549 
550  T operator()(const utils::monostate&) const { return def_; }
551  T operator()(bool) const { return def_; }
552  T operator()(int i) const { return static_cast<T>(i); }
553  T operator()(unsigned long long u) const { return static_cast<T>(u); }
554  T operator()(double d) const { return static_cast<T>(d); }
555  T operator()(const t_string&) const { return def_; }
556  T operator()(const std::string& s) const
557  {
558  if(s.size() >= 2 && s[0] == '(') {
559  return formula_handler_(s);
560  }
561  return lexical_cast_default<T>(s, def_);
562  }
563 
564 private:
565  const T def_;
566  const TFuncFormula& formula_handler_;
567 };
568 
569 template<typename T, typename TFuncFormula>
570 T get_single_ability_value(const config::attribute_value& v, T def, const unit_ability& ability_info, const map_location& receiver_loc, const_attack_ptr att, const TFuncFormula& formula_handler)
571 {
572  return v.apply_visitor(get_ability_value_visitor(def, [&](const std::string& s) {
573 
574  try {
575  const unit_map& units = get_unit_map();
576 
577  auto u_itor = units.find(ability_info.teacher_loc);
578 
579  if(u_itor == units.end()) {
580  return def;
581  }
582  wfl::map_formula_callable callable(std::make_shared<wfl::unit_callable>(*u_itor));
583  if(att) {
584  att->add_formula_context(callable);
585  }
586  if (auto uptr = units.find_unit_ptr(ability_info.student_loc)) {
587  callable.add("student", wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
588  }
589  if (auto uptr = units.find_unit_ptr(receiver_loc)) {
590  callable.add("other", wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
591  }
592  return formula_handler(wfl::formula(s, new wfl::gamestate_function_symbol_table), callable);
593  } catch(const wfl::formula_error& e) {
594  lg::log_to_chat() << "Formula error in ability or weapon special: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
595  ERR_WML << "Formula error in ability or weapon special: " << e.type << " at " << e.filename << ':' << e.line << ")";
596  return def;
597  }
598  }));
599 }
600 }
601 
602 template<typename TComp>
603 std::pair<int,map_location> unit_ability_list::get_extremum(const std::string& key, int def, const TComp& comp) const
604 {
605  if ( cfgs_.empty() ) {
606  return std::pair(def, map_location());
607  }
608  // The returned location is the best non-cumulative one, if any,
609  // the best absolute cumulative one otherwise.
610  map_location best_loc;
611  bool only_cumulative = true;
612  int abs_max = 0;
613  int flat = 0;
614  int stack = 0;
615  for (const unit_ability& p : cfgs_)
616  {
617  int value = get_single_ability_value((*p.ability_cfg)[key], def, p, loc(), const_attack_ptr(), [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
618  return formula.evaluate(callable).as_int();
619  });
620 
621  if ((*p.ability_cfg)["cumulative"].to_bool()) {
622  stack += value;
623  if (value < 0) value = -value;
624  if (only_cumulative && !comp(value, abs_max)) {
625  abs_max = value;
626  best_loc = p.teacher_loc;
627  }
628  } else if (only_cumulative || comp(flat, value)) {
629  only_cumulative = false;
630  flat = value;
631  best_loc = p.teacher_loc;
632  }
633  }
634  return std::pair(flat + stack, best_loc);
635 }
636 
637 template std::pair<int, map_location> unit_ability_list::get_extremum<std::less<int>>(const std::string& key, int def, const std::less<int>& comp) const;
638 template std::pair<int, map_location> unit_ability_list::get_extremum<std::greater<int>>(const std::string& key, int def, const std::greater<int>& comp) const;
639 
640 /*
641  *
642  * [special]
643  * [swarm]
644  * name= _ "swarm"
645  * name_inactive= _ ""
646  * description= _ ""
647  * description_inactive= _ ""
648  * cumulative=no
649  * apply_to=self #self,opponent,defender,attacker,both
650  * #active_on=defense # or offense; omitting this means "both"
651  *
652  * swarm_attacks_max=4
653  * swarm_attacks_min=2
654  *
655  * [filter_self] // SUF
656  * ...
657  * [/filter_self]
658  * [filter_opponent] // SUF
659  * [filter_attacker] // SUF
660  * [filter_defender] // SUF
661  * [filter_adjacent] // SAUF
662  * [filter_adjacent_location] // SAUF + locs
663  * [/swarm]
664  * [/special]
665  *
666  */
667 
668 namespace {
669 
670  struct special_match
671  {
672  std::string tag_name;
673  const config* cfg;
674  };
675 
676  /**
677  * Gets the children of @parent (which should be the specials for an
678  * attack_type) and places the ones whose tag or id= matches @a id into
679  * @a tag_result and @a id_result.
680  *
681  * If @a just_peeking is set to true, then @a tag_result and @a id_result
682  * are not touched; instead the return value is used to indicate if any
683  * matching children were found.
684  *
685  * @returns true if @a just_peeking is true and a match was found;
686  * false otherwise.
687  */
688  bool get_special_children(std::vector<special_match>& tag_result,
689  std::vector<special_match>& id_result,
690  const config& parent, const std::string& id,
691  bool just_peeking=false) {
692  for (const config::any_child sp : parent.all_children_range())
693  {
694  if (just_peeking && (sp.key == id || sp.cfg["id"] == id)) {
695  return true; // peek succeeded; done
696  }
697 
698  if(sp.key == id) {
699  special_match special = { sp.key, &sp.cfg };
700  tag_result.push_back(special);
701  }
702  if(sp.cfg["id"] == id) {
703  special_match special = { sp.key, &sp.cfg };
704  id_result.push_back(special);
705  }
706  }
707  return false;
708  }
709 
710  bool get_special_children_id(std::vector<special_match>& id_result,
711  const config& parent, const std::string& id,
712  bool just_peeking=false) {
713  for (const config::any_child sp : parent.all_children_range())
714  {
715  if (just_peeking && (sp.cfg["id"] == id)) {
716  return true; // peek succeeded; done
717  }
718 
719  if(sp.cfg["id"] == id) {
720  special_match special = { sp.key, &sp.cfg };
721  id_result.push_back(special);
722  }
723  }
724  return false;
725  }
726 
727  bool get_special_children_tags(std::vector<special_match>& tag_result,
728  const config& parent, const std::string& id,
729  bool just_peeking=false) {
730  for (const config::any_child sp : parent.all_children_range())
731  {
732  if (just_peeking && (sp.key == id)) {
733  return true; // peek succeeded; done
734  }
735 
736  if(sp.key == id) {
737  special_match special = { sp.key, &sp.cfg };
738  tag_result.push_back(special);
739  }
740  }
741  return false;
742  }
743 }
744 
745 /**
746  * Returns whether or not @a *this has a special with a tag or id equal to
747  * @a special. If @a simple_check is set to true, then the check is merely
748  * for being present. Otherwise (the default), the check is for a special
749  * active in the current context (see set_specials_context), including
750  * specials obtained from the opponent's attack.
751  */
752 bool attack_type::has_special(const std::string& special, bool simple_check, bool special_id, bool special_tags) const
753 {
754  {
755  std::vector<special_match> special_tag_matches;
756  std::vector<special_match> special_id_matches;
757  if(special_id && special_tags){
758  if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
759  return true;
760  }
761  } else if(special_id && !special_tags){
762  if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
763  return true;
764  }
765  } else if(!special_id && special_tags){
766  if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
767  return true;
768  }
769  }
770  // If we make it to here, then either list.empty() or !simple_check.
771  // So if the list is not empty, then this is not a simple check and
772  // we need to check each special in the list to see if any are active.
773  if(special_tags){
774  for(const special_match& entry : special_tag_matches) {
775  if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
776  return true;
777  }
778  }
779  }
780  if(special_id){
781  for(const special_match& entry : special_id_matches) {
782  if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
783  return true;
784  }
785  }
786  }
787  }
788 
789  // Skip checking the opponent's attack?
790  if ( simple_check || !other_attack_ ) {
791  return false;
792  }
793 
794  std::vector<special_match> special_tag_matches;
795  std::vector<special_match> special_id_matches;
796  if(special_id && special_tags){
797  get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
798  } else if(special_id && !special_tags){
799  get_special_children_id(special_id_matches, other_attack_->specials_, special);
800  } else if(!special_id && special_tags){
801  get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
802  }
803  if(special_tags){
804  for(const special_match& entry : special_tag_matches) {
805  if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
806  return true;
807  }
808  }
809  }
810  if(special_id){
811  for(const special_match& entry : special_id_matches) {
812  if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
813  return true;
814  }
815  }
816  }
817  return false;
818 }
819 
820 /**
821  * Returns the currently active specials as an ability list, given the current
822  * context (see set_specials_context).
823  */
824 unit_ability_list attack_type::get_specials(const std::string& special) const
825 {
826  //log_scope("get_specials");
827  const map_location loc = self_ ? self_->get_location() : self_loc_;
828  unit_ability_list res(loc);
829 
830  for(const config& i : specials_.child_range(special)) {
831  if(special_active(i, AFFECT_SELF, special)) {
832  res.emplace_back(&i, loc, loc);
833  }
834  }
835 
836  if(!other_attack_) {
837  return res;
838  }
839 
840  for(const config& i : other_attack_->specials_.child_range(special)) {
841  if(other_attack_->special_active(i, AFFECT_OTHER, special)) {
843  }
844  }
845  return res;
846 }
847 
848 /**
849  * Returns a vector of names and descriptions for the specials of *this.
850  * Each std::pair in the vector has first = name and second = description.
851  *
852  * This uses either the active or inactive name/description for each special,
853  * based on the current context (see set_specials_context), provided
854  * @a active_list is not nullptr. Otherwise specials are assumed active.
855  * If the appropriate name is empty, the special is skipped.
856  */
857 std::vector<std::pair<t_string, t_string>> attack_type::special_tooltips(
858  boost::dynamic_bitset<>* active_list) const
859 {
860  //log_scope("special_tooltips");
861  std::vector<std::pair<t_string, t_string>> res;
862  if ( active_list )
863  active_list->clear();
864 
866  {
867  if ( !active_list || special_active(sp.cfg, AFFECT_EITHER, sp.key) ) {
868  const t_string &name = sp.cfg["name"];
869  if (!name.empty()) {
870  res.emplace_back(name, sp.cfg["description"].t_str() );
871  if ( active_list )
872  active_list->push_back(true);
873  }
874  } else {
875  const t_string& name = sp.cfg.get_or("name_inactive", "name").t_str();
876  if (!name.empty()) {
877  res.emplace_back(name, sp.cfg.get_or("description_inactive", "description").t_str() );
878  active_list->push_back(false);
879  }
880  }
881  }
882  return res;
883 }
884 
885 /**
886  * static used in weapon_specials (bool only_active) and
887  * @return a string and a set_string for the weapon_specials function below.
888  * @param[in,out] temp_string the string modified and returned
889  * @param[in] active the boolean for determine if @name can be added or not
890  * @param[in] name string who must be or not added
891  * @param[in,out] checking_name the reference for checking if @name already added
892  */
893 static void add_name(std::string& temp_string, bool active, const std::string name, std::set<std::string>& checking_name)
894 {
895  if (active) {
896  if (!name.empty() && checking_name.count(name) == 0) {
897  checking_name.insert(name);
898  if (!temp_string.empty()) temp_string += ", ";
899  temp_string += font::span_color(font::BUTTON_COLOR, name);
900  }
901  }
902 }
903 
904 /**
905  * Returns a comma-separated string of active names for the specials of *this.
906  * Empty names are skipped.
907  *
908  * Whether or not a special is active depends
909  * on the current context (see set_specials_context)
910  */
911 std::string attack_type::weapon_specials() const
912 {
913  //log_scope("weapon_specials");
914  std::string res;
916  {
917  const bool active = special_active(sp.cfg, AFFECT_EITHER, sp.key);
918 
919  const std::string& name =
920  active
921  ? sp.cfg["name"].str()
922  : sp.cfg.get_or("name_inactive", "name").str();
923  if (!name.empty()) {
924  if (!res.empty()) res += ", ";
925  if (!active) res += font::span_color(font::inactive_details_color);
926  res += name;
927  if (!active) res += "</span>";
928  }
929  }
930  std::string temp_string;
931  std::set<std::string> checking_name;
932  weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
933  weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {}, "affect_allies");
934  if(!temp_string.empty() && !res.empty()) {
935  temp_string = ", \n" + temp_string;
936  res += temp_string;
937  } else if (!temp_string.empty()){
938  res = temp_string;
939  }
940  return res;
941 }
942 
943 static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name, const std::string from_str)
944 {
945  if(!temp_string.empty()){
946  temp_string = from_str.c_str() + temp_string;
947  weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ? "\n" : "";
948  weapon_abilities += temp_string;
949  temp_string.clear();
950  checking_name.clear();
951  }
952 }
953 
954 std::string attack_type::weapon_specials_value(const std::set<std::string> checking_tags) const
955 {
956  //log_scope("weapon_specials_value");
957  std::string temp_string, weapon_abilities;
958  std::set<std::string> checking_name;
960  if((checking_tags.count(sp.key) != 0)){
961  const bool active = special_active(sp.cfg, AFFECT_SELF, sp.key);
962  add_name(temp_string, active, sp.cfg["name"].str(), checking_name);
963  }
964  }
965  add_name_list(temp_string, weapon_abilities, checking_name, "");
966 
967  weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags, true);
968  add_name_list(temp_string, weapon_abilities, checking_name, _("Owned: "));
969 
970  weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags, "affect_allies", true);
971  // TRANSLATORS: Past-participle of "teach", used for an ability similar to leadership
972  add_name_list(temp_string, weapon_abilities, checking_name, _("Taught: "));
973 
974  weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags, "affect_enemies", true);
975  // TRANSLATORS: Past-participle of "teach", used for an ability similar to leadership
976  add_name_list(temp_string, weapon_abilities, checking_name, _("Taught: (by an enemy): "));
977 
978 
979  if(other_attack_) {
980  for (const config::any_child sp : other_attack_->specials_.all_children_range()) {
981  if((checking_tags.count(sp.key) != 0)){
982  const bool active = other_attack_->special_active(sp.cfg, AFFECT_OTHER, sp.key);
983  add_name(temp_string, active, sp.cfg["name"].str(), checking_name);
984  }
985  }
986  }
987  weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
988  weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
989  add_name_list(temp_string, weapon_abilities, checking_name, _("Used by opponent: "));
990 
991  return weapon_abilities;
992 }
993 
995  std::string& temp_string,
996  unit_const_ptr self,
997  const_attack_ptr self_attack,
998  const_attack_ptr other_attack,
999  const map_location& self_loc,
1000  AFFECTS whom,
1001  std::set<std::string>& checking_name,
1002  const std::set<std::string>& checking_tags,
1003  bool leader_bool)
1004 {
1005  if(self){
1006  for (const config::any_child sp : self->abilities().all_children_range()){
1007  bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(sp.key) != 0) : true;
1008  const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack, sp.cfg, self, self_loc, whom, sp.key, leader_bool);
1009  add_name(temp_string, active, sp.cfg["name"].str(), checking_name);
1010  }
1011  }
1012 }
1013 
1015  std::string& temp_string,
1016  unit_const_ptr self,
1017  const_attack_ptr self_attack,
1018  const_attack_ptr other_attack,
1019  const map_location& self_loc,
1020  AFFECTS whom,
1021  std::set<std::string>& checking_name,
1022  const std::set<std::string>& checking_tags,
1023  const std::string& affect_adjacents,
1024  bool leader_bool)
1025 {
1026  const unit_map& units = get_unit_map();
1027  if(self){
1028  const auto adjacent = get_adjacent_tiles(self_loc);
1029  for(unsigned i = 0; i < adjacent.size(); ++i) {
1030  const unit_map::const_iterator it = units.find(adjacent[i]);
1031  if (it == units.end() || it->incapacitated())
1032  continue;
1033  if(&*it == self.get())
1034  continue;
1035  for (const config::any_child sp : it->abilities().all_children_range()){
1036  bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(sp.key) != 0) : true;
1037  bool default_bool = (affect_adjacents == "affect_allies") ? true : false;
1038  bool affect_allies = (!affect_adjacents.empty()) ? sp.cfg[affect_adjacents].to_bool(default_bool) : true;
1039  const bool active = tag_checked && check_adj_abilities_impl(self_attack, other_attack, sp.cfg, self, *it, i, self_loc, whom, sp.key, leader_bool) && affect_allies;
1040  add_name(temp_string, active, sp.cfg["name"].str(), checking_name);
1041  }
1042  }
1043  }
1044 }
1045 
1046 
1047 /**
1048  * Sets the context under which specials will be checked for being active.
1049  * This version is appropriate if both units in a combat are known.
1050  * @param[in] weapon The weapon being considered.
1051  * @param[in] self A reference to the unit with this weapon.
1052  * @param[in] other A reference to the other unit in the combat.
1053  * @param[in] unit_loc The location of the unit with this weapon.
1054  * @param[in] other_loc The location of the other unit in the combat.
1055  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
1056  * @param[in] other_attack The attack used by the other unit.
1057  */
1059  const attack_type& weapon,
1060  const_attack_ptr other_attack,
1061  unit_const_ptr self,
1062  unit_const_ptr other,
1063  const map_location& unit_loc,
1064  const map_location& other_loc,
1065  bool attacking)
1066  : parent(weapon.shared_from_this())
1067 {
1068  weapon.self_ = self;
1069  weapon.other_ = other;
1070  weapon.self_loc_ = unit_loc;
1071  weapon.other_loc_ = other_loc;
1072  weapon.is_attacker_ = attacking;
1073  weapon.other_attack_ = other_attack;
1074  weapon.is_for_listing_ = false;
1075 }
1076 
1077 /**
1078  * Sets the context under which specials will be checked for being active.
1079  * This version is appropriate if there is no specific combat being considered.
1080  * @param[in] weapon The weapon being considered.
1081  * @param[in] self A reference to the unit with this weapon.
1082  * @param[in] loc The location of the unit with this weapon.
1083  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
1084  */
1086  : parent(weapon.shared_from_this())
1087 {
1088  weapon.self_ = self;
1089  weapon.other_ = unit_ptr();
1090  weapon.self_loc_ = loc;
1092  weapon.is_attacker_ = attacking;
1093  weapon.other_attack_ = nullptr;
1094  weapon.is_for_listing_ = false;
1095 }
1096 
1097 /**
1098  * Sets the context under which specials will be checked for being active.
1099  * This version is appropriate for theoretical units of a particular type.
1100  * @param[in] weapon The weapon being considered.
1101  * @param[in] self_type A reference to the type of the unit with this weapon.
1102  * @param[in] loc The location of the unit with this weapon.
1103  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
1104  */
1105 attack_type::specials_context_t::specials_context_t(const attack_type& weapon, const unit_type& self_type, const map_location& loc, bool attacking)
1106  : parent(weapon.shared_from_this())
1107 {
1108  UNUSED(self_type);
1109  weapon.self_ = unit_ptr();
1110  weapon.other_ = unit_ptr();
1111  weapon.self_loc_ = loc;
1113  weapon.is_attacker_ = attacking;
1114  weapon.other_attack_ = nullptr;
1115  weapon.is_for_listing_ = false;
1116 }
1117 
1119  : parent(weapon.shared_from_this())
1120 {
1121  weapon.is_for_listing_ = true;
1122  weapon.is_attacker_ = attacking;
1123 }
1124 
1126 {
1127  if(was_moved) return;
1128  parent->self_ = unit_ptr();
1129  parent->other_ = unit_ptr();
1130  parent->self_loc_ = map_location::null_location();
1131  parent->other_loc_ = map_location::null_location();
1132  parent->is_attacker_ = false;
1133  parent->other_attack_ = nullptr;
1134  parent->is_for_listing_ = false;
1135 }
1136 
1138  : parent(other.parent)
1139 {
1140  other.was_moved = true;
1141 }
1142 
1143 /**
1144  * Calculates the number of attacks this weapon has, considering specials.
1145  * This returns two numbers because of the swarm special. The actual number of
1146  * attacks depends on the unit's health and should be:
1147  * min_attacks + (max_attacks - min_attacks) * (current hp) / (max hp)
1148  * c.f. swarm_blows()
1149  */
1150 void attack_type::modified_attacks(unsigned & min_attacks,
1151  unsigned & max_attacks) const
1152 {
1153  // Apply [attacks].
1154  int attacks_value = composite_value(get_specials_and_abilities("attacks"), num_attacks());
1155 
1156  if ( attacks_value < 0 ) {
1157  attacks_value = num_attacks();
1158  ERR_NG << "negative number of strikes after applying weapon specials";
1159  }
1160 
1161  // Apply [swarm].
1162  unit_ability_list swarm_specials = get_specials_and_abilities("swarm");
1163  if ( !swarm_specials.empty() ) {
1164  min_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_min").first);
1165  max_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_max", attacks_value).first);
1166  } else {
1167  min_attacks = max_attacks = attacks_value;
1168  }
1169 }
1170 
1171 static std::string select_replacement_type(const unit_ability_list& damage_type_list)
1172 {
1173  std::map<std::string, unsigned int> type_count;
1174  unsigned int max = 0;
1175  for(auto& i : damage_type_list) {
1176  const config& c = *i.ability_cfg;
1177  if(c.has_attribute("replacement_type")) {
1178  std::string type = c["replacement_type"].str();
1179  unsigned int count = ++type_count[type];
1180  if((count > max)) {
1181  max = count;
1182  }
1183  }
1184  }
1185 
1186  if (type_count.empty()) return "";
1187 
1188  std::vector<std::string> type_list;
1189  for(auto& i : type_count){
1190  if(i.second == max){
1191  type_list.push_back(i.first);
1192  }
1193  }
1194 
1195  if(type_list.empty()) return "";
1196 
1197  return type_list.front();
1198 }
1199 
1200 static std::string select_alternative_type(const unit_ability_list& damage_type_list, unit_ability_list resistance_list, const unit& u)
1201 {
1202  std::map<std::string, int> type_res;
1203  int max_res = std::numeric_limits<int>::min();
1204  for(auto& i : damage_type_list) {
1205  const config& c = *i.ability_cfg;
1206  if(c.has_attribute("alternative_type")) {
1207  std::string type = c["alternative_type"].str();
1208  if(type_res.count(type) == 0){
1209  type_res[type] = u.resistance_value(resistance_list, type);
1210  max_res = std::max(max_res, type_res[type]);
1211  }
1212  }
1213  }
1214 
1215  if (type_res.empty()) return "";
1216 
1217  std::vector<std::string> type_list;
1218  for(auto& i : type_res){
1219  if(i.second == max_res){
1220  type_list.push_back(i.first);
1221  }
1222  }
1223  if(type_list.empty()) return "";
1224 
1225  return type_list.front();
1226 }
1227 
1228 std::string attack_type::select_damage_type(const unit_ability_list& damage_type_list, const std::string& key_name, unit_ability_list resistance_list) const
1229 {
1230  bool is_alternative = (key_name == "alternative_type");
1231  if(is_alternative && other_){
1232  return select_alternative_type(damage_type_list, resistance_list, (*other_));
1233  } else if(!is_alternative){
1234  return select_replacement_type(damage_type_list);
1235  }
1236  return "";
1237 }
1238 
1239 /**
1240  * Returns the type of damage inflicted.
1241  */
1242 std::pair<std::string, std::string> attack_type::damage_type() const
1243 {
1244  if(attack_empty()){
1245  return {"", ""};
1246  }
1247  unit_ability_list damage_type_list = get_specials_and_abilities("damage_type");
1248  if(damage_type_list.empty()){
1249  return {type(), ""};
1250  }
1251 
1252  unit_ability_list resistance_list;
1253  if(other_){
1254  resistance_list = (*other_).get_abilities_weapons("resistance", other_loc_, other_attack_, shared_from_this());
1255  }
1256  std::string replacement_type = select_damage_type(damage_type_list, "replacement_type", resistance_list);
1257  std::string alternative_type = select_damage_type(damage_type_list, "alternative_type", resistance_list);
1258  std::string type_damage = replacement_type.empty() ? type() : replacement_type;
1259  if(!alternative_type.empty() && type_damage != alternative_type){
1260  return {type_damage, alternative_type};
1261  }
1262  return {type_damage, ""};
1263 }
1264 
1265 std::set<std::string> attack_type::alternative_damage_types() const
1266 {
1267  unit_ability_list damage_type_list = get_specials_and_abilities("damage_type");
1268  if(damage_type_list.empty()){
1269  return {};
1270  }
1271  std::set<std::string> damage_types;
1272  for(auto& i : damage_type_list) {
1273  const config& c = *i.ability_cfg;
1274  if(c.has_attribute("alternative_type")){
1275  damage_types.insert(c["alternative_type"].str());
1276  }
1277  }
1278 
1279  return damage_types;
1280 }
1281 
1282 
1283 /**
1284  * Returns the damage per attack of this weapon, considering specials.
1285  */
1287 {
1288  int damage_value = composite_value(get_specials_and_abilities("damage"), damage());
1289  return damage_value;
1290 }
1291 
1292 
1293 namespace { // Helpers for attack_type::special_active()
1294 
1295  /**
1296  * Returns whether or not the given special affects the opponent of the unit
1297  * with the special.
1298  * @param[in] special a weapon special WML structure
1299  * @param[in] is_attacker whether or not the unit with the special is the attacker
1300  */
1301  bool special_affects_opponent(const config& special, bool is_attacker)
1302  {
1303  //log_scope("special_affects_opponent");
1304  const std::string& apply_to = special["apply_to"];
1305  if ( apply_to.empty() )
1306  return false;
1307  if ( apply_to == "both" )
1308  return true;
1309  if ( apply_to == "opponent" )
1310  return true;
1311  if ( is_attacker && apply_to == "defender" )
1312  return true;
1313  if ( !is_attacker && apply_to == "attacker" )
1314  return true;
1315  return false;
1316  }
1317 
1318  /**
1319  * Returns whether or not the given special affects the unit with the special.
1320  * @param[in] special a weapon special WML structure
1321  * @param[in] is_attacker whether or not the unit with the special is the attacker
1322  */
1323  bool special_affects_self(const config& special, bool is_attacker)
1324  {
1325  //log_scope("special_affects_self");
1326  const std::string& apply_to = special["apply_to"];
1327  if ( apply_to.empty() )
1328  return true;
1329  if ( apply_to == "both" )
1330  return true;
1331  if ( apply_to == "self" )
1332  return true;
1333  if ( is_attacker && apply_to == "attacker" )
1334  return true;
1335  if ( !is_attacker && apply_to == "defender")
1336  return true;
1337  return false;
1338  }
1339 
1340  /**
1341  * Determines if a unit/weapon combination matches the specified child
1342  * (normally a [filter_*] child) of the provided filter.
1343  * @param[in] u A unit to filter.
1344  * @param[in] u2 Another unit to filter.
1345  * @param[in] loc The presumed location of @a unit.
1346  * @param[in] weapon The attack_type to filter.
1347  * @param[in] filter The filter containing the child filter to use.
1348  * @param[in] for_listing
1349  * @param[in] child_tag The tag of the child filter to use.
1350  * @param[in] tag_name Parameter used for don't have infinite recusion for some filter attribute.
1351  */
1352  static bool special_unit_matches(unit_const_ptr & u,
1353  unit_const_ptr & u2,
1354  const map_location & loc,
1355  const_attack_ptr weapon,
1356  const config & filter,
1357  const bool for_listing,
1358  const std::string & child_tag, const std::string& tag_name)
1359  {
1360  if (for_listing && !loc.valid())
1361  // The special's context was set to ignore this unit, so assume we pass.
1362  // (This is used by reports.cpp to show active specials when the
1363  // opponent is not known. From a player's perspective, the special
1364  // is active, in that it can be used, even though the player might
1365  // need to select an appropriate opponent.)
1366  return true;
1367 
1368  auto filter_child = filter.optional_child(child_tag);
1369  if ( !filter_child )
1370  // The special does not filter on this unit, so we pass.
1371  return true;
1372 
1373  // If the primary unit doesn't exist, there's nothing to match
1374  if (!u) {
1375  return false;
1376  }
1377 
1378  unit_filter ufilt{vconfig(*filter_child)};
1379 
1380  // If the other unit doesn't exist, try matching without it
1381 
1382 
1383  // Check for a weapon match.
1384  if (auto filter_weapon = filter_child->optional_child("filter_weapon") ) {
1385  if ( !weapon || !weapon->matches_filter(*filter_weapon, tag_name) )
1386  return false;
1387  }
1388 
1389  // Passed.
1390  // If the other unit doesn't exist, try matching without it
1391  if (!u2) {
1392  return ufilt.matches(*u, loc);
1393  }
1394  return ufilt.matches(*u, loc, *u2);
1395  }
1396 
1397 }//anonymous namespace
1398 
1399 
1400 //The following functions are intended to allow the use in combat of capacities
1401 //identical to special weapons and therefore to be able to use them on adjacent
1402 //units (abilities of type 'aura') or else on all types of weapons even if the
1403 //beneficiary unit does not have a corresponding weapon
1404 //(defense against ranged weapons abilities for a unit that only has melee attacks)
1405 
1406 unit_ability_list attack_type::get_weapon_ability(const std::string& ability) const
1407 {
1408  const map_location loc = self_ ? self_->get_location() : self_loc_;
1409  unit_ability_list abil_list(loc);
1410  if(self_) {
1411  abil_list.append_if((*self_).get_abilities(ability, self_loc_), [&](const unit_ability& i) {
1412  return special_active(*i.ability_cfg, AFFECT_SELF, ability, "filter_student");
1413  });
1414  }
1415 
1416  if(other_) {
1417  abil_list.append_if((*other_).get_abilities(ability, other_loc_), [&](const unit_ability& i) {
1418  return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, "filter_student");
1419  });
1420  }
1421 
1422  return abil_list;
1423 }
1424 
1426 {
1427  // get all weapon specials of the provided type
1428  unit_ability_list abil_list = get_specials(special);
1429  // append all such weapon specials as abilities as well
1430  abil_list.append(get_weapon_ability(special));
1431  // get a list of specials/"specials as abilities" that may potentially overwrite others
1432  unit_ability_list overwriters = overwrite_special_overwriter(abil_list, special);
1433  if(!abil_list.empty() && !overwriters.empty()){
1434  // remove all abilities that would be overwritten
1435  utils::erase_if(abil_list, [&](const unit_ability& j) {
1436  return (overwrite_special_checking(overwriters, *j.ability_cfg, special));
1437  });
1438  }
1439  return abil_list;
1440 }
1441 
1442 int attack_type::composite_value(const unit_ability_list& abil_list, int base_value) const
1443 {
1444  return unit_abilities::effect(abil_list, base_value, shared_from_this()).get_composite_value();
1445 }
1446 
1447 static bool overwrite_special_affects(const config& special)
1448 {
1449  const std::string& apply_to = special["overwrite_specials"];
1450  return (apply_to == "one_side" || apply_to == "both_sides");
1451 }
1452 
1454 {
1455  //remove element without overwrite_specials key, if list empty after check return empty list.
1456  utils::erase_if(overwriters, [&](const unit_ability& i) {
1457  return (!overwrite_special_affects(*i.ability_cfg));
1458  });
1459 
1460  // if empty, nothing is doing any overwriting
1461  if(overwriters.empty()){
1462  return overwriters;
1463  }
1464 
1465  // if there are specials/"specials as abilities" that could potentially overwrite each other
1466  if(overwriters.size() >= 2){
1467  // sort them by overwrite priority from highest to lowest (default priority is 0)
1468  utils::sort_if(overwriters,[](const unit_ability& i, const unit_ability& j){
1469  auto oi = (*i.ability_cfg).optional_child("overwrite");
1470  double l = 0;
1471  if(oi && !oi["priority"].empty()){
1472  l = oi["priority"].to_double(0);
1473  }
1474  auto oj = (*j.ability_cfg).optional_child("overwrite");
1475  double r = 0;
1476  if(oj && !oj["priority"].empty()){
1477  r = oj["priority"].to_double(0);
1478  }
1479  return l > r;
1480  });
1481  // remove any that need to be overwritten
1482  utils::erase_if(overwriters, [&](const unit_ability& i) {
1483  return (overwrite_special_checking(overwriters, *i.ability_cfg, tag_name));
1484  });
1485  }
1486  return overwriters;
1487 }
1488 
1489 bool attack_type::overwrite_special_checking(unit_ability_list& overwriters, const config& cfg, const std::string& tag_name) const
1490 {
1491  if(overwriters.empty()){
1492  return false;
1493  }
1494 
1495  for(const auto& j : overwriters) {
1496  // whether the overwriter affects a single side
1497  bool affect_side = ((*j.ability_cfg)["overwrite_specials"] == "one_side");
1498  // the overwriter's priority, default of 0
1499  auto overwrite_specials = (*j.ability_cfg).optional_child("overwrite");
1500  double priority = overwrite_specials ? overwrite_specials["priority"].to_double(0) : 0.00;
1501  // the cfg being checked for whether it will be overwritten
1502  auto has_overwrite_specials = cfg.optional_child("overwrite");
1503  // if the overwriter's priority is greater than 0, then true if the cfg being checked has a higher priority
1504  // else true
1505  bool prior = (priority > 0) ? (has_overwrite_specials && has_overwrite_specials["priority"].to_double(0) >= priority) : true;
1506  // true if the cfg being checked affects one or both sides and doesn't have a higher priority, or if it doesn't affect one or both sides
1507  // aka whether the cfg being checked can potentially be overwritten by the current overwriter
1508  bool is_overwritable = (overwrite_special_affects(cfg) && !prior) || !overwrite_special_affects(cfg);
1509  bool one_side_overwritable = true;
1510 
1511  // if the current overwriter affects one side and the cfg being checked can be overwritten by this overwriter
1512  // then check that the current overwriter and the cfg being checked both affect either this unit or its opponent
1513  if(affect_side && is_overwritable){
1514  if(special_affects_self(*j.ability_cfg, is_attacker_)){
1515  one_side_overwritable = special_affects_self(cfg, is_attacker_);
1516  }
1517  else if(special_affects_opponent(*j.ability_cfg, !is_attacker_)){
1518  one_side_overwritable = special_affects_opponent(cfg, !is_attacker_);
1519  }
1520  }
1521 
1522  // check whether the current overwriter is disabled due to a filter
1523  bool special_matches = true;
1524  if(overwrite_specials){
1525  auto overwrite_filter = (*overwrite_specials).optional_child("experimental_filter_specials");
1526  if(overwrite_filter && is_overwritable && one_side_overwritable){
1527  if(self_){
1528  special_matches = (*self_).ability_matches_filter(cfg, tag_name, *overwrite_filter);
1529  }
1530  }
1531  }
1532 
1533  // if the cfg being checked should be overwritten
1534  // and either this unit or its opponent are affected
1535  // and the current overwriter is not disabled due to a filter
1536  if(is_overwritable && one_side_overwritable && special_matches){
1537  return true;
1538  }
1539  }
1540  return false;
1541 }
1542 
1543  /**
1544  * Gets the children of parent (which should be the abilities for an
1545  * attack_type) and places the ones whose tag or id= matches @a id into
1546  * @a tag_result and @a id_result.
1547  * @param tag_result receive the children whose tag matches @a id
1548  * @param id_result receive the children whose id matches @a id
1549  * @param parent the tags whose contain children (abilities here)
1550  * @param id tag or id of child tested
1551  * @param special_id if true, children check by id
1552  * @param special_tags if true, children check by tags
1553  */
1554 static void get_ability_children(std::vector<special_match>& tag_result,
1555  std::vector<special_match>& id_result,
1556  const config& parent, const std::string& id,
1557  bool special_id=true, bool special_tags=true) {
1558  if(special_id && special_tags){
1559  get_special_children(tag_result, id_result, parent, id);
1560  } else if(special_id && !special_tags){
1561  get_special_children_id(id_result, parent, id);
1562  } else if(!special_id && special_tags){
1563  get_special_children_tags(tag_result, parent, id);
1564  }
1565 }
1566 
1567 bool unit::get_self_ability_bool(const config& special, const std::string& tag_name, const map_location& loc) const
1568 {
1569  return (ability_active(tag_name, special, loc) && ability_affects_self(tag_name, special, loc));
1570 }
1571 
1572 bool unit::get_adj_ability_bool(const config& special, const std::string& tag_name, int dir, const map_location& loc, const unit& from) const
1573 {
1574  const auto adjacent = get_adjacent_tiles(loc);
1575  return (affects_side(special, side(), from.side()) && from.ability_active(tag_name, special, adjacent[dir]) && ability_affects_adjacent(tag_name, special, dir, loc, from));
1576 }
1577 
1578 bool unit::get_self_ability_bool_weapon(const config& special, const std::string& tag_name, const map_location& loc, const_attack_ptr weapon, const_attack_ptr opp_weapon) const
1579 {
1580  return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon, false) && ability_affects_weapon(special, opp_weapon, true));
1581 }
1582 
1583 bool unit::get_adj_ability_bool_weapon(const config& special, const std::string& tag_name, int dir, const map_location& loc, const unit& from, const_attack_ptr weapon, const_attack_ptr opp_weapon) const
1584 {
1585  return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon, false) && ability_affects_weapon(special, opp_weapon, true));
1586 }
1587 
1588 bool attack_type::check_self_abilities(const config& cfg, const std::string& special) const
1589 {
1590  return check_self_abilities_impl(shared_from_this(), other_attack_, cfg, self_, self_loc_, AFFECT_SELF, special, true);
1591 }
1592 
1593 bool attack_type::check_self_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config& special, unit_const_ptr u, const map_location& loc, AFFECTS whom, const std::string& tag_name, bool leader_bool)
1594 {
1595  if(tag_name == "leadership" && leader_bool){
1596  if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
1597  return true;
1598  }
1599  }
1600  if((*u).checking_tags().count(tag_name) != 0){
1601  if((*u).get_self_ability_bool(special, tag_name, loc) && special_active_impl(self_attack, other_attack, special, whom, tag_name, "filter_student")) {
1602  return true;
1603  }
1604  }
1605  return false;
1606 }
1607 
1608 bool attack_type::check_adj_abilities(const config& cfg, const std::string& special, int dir, const unit& from) const
1609 {
1610  return check_adj_abilities_impl(shared_from_this(), other_attack_, cfg, self_, from, dir, self_loc_, AFFECT_SELF, special, true);
1611 }
1612 
1613 bool attack_type::check_adj_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config& special, unit_const_ptr u, const unit& from, int dir, const map_location& loc, AFFECTS whom, const std::string& tag_name, bool leader_bool)
1614 {
1615  if(tag_name == "leadership" && leader_bool){
1616  if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
1617  return true;
1618  }
1619  }
1620  if((*u).checking_tags().count(tag_name) != 0){
1621  if((*u).get_adj_ability_bool(special, tag_name, dir, loc, from) && special_active_impl(self_attack, other_attack, special, whom, tag_name, "filter_student")) {
1622  return true;
1623  }
1624  }
1625  return false;
1626 }
1627 /**
1628  * Returns whether or not @a *this has a special ability with a tag or id equal to
1629  * @a special. the Check is for a special ability
1630  * active in the current context (see set_specials_context), including
1631  * specials obtained from the opponent's attack.
1632  */
1633 bool attack_type::has_weapon_ability(const std::string& special, bool special_id, bool special_tags) const
1634 {
1635  const unit_map& units = get_unit_map();
1636  if(self_){
1637  std::vector<special_match> special_tag_matches_self;
1638  std::vector<special_match> special_id_matches_self;
1639  get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1640  if(special_tags){
1641  for(const special_match& entry : special_tag_matches_self) {
1642  if(check_self_abilities(*entry.cfg, entry.tag_name)){
1643  return true;
1644  }
1645  }
1646  }
1647  if(special_id){
1648  for(const special_match& entry : special_id_matches_self) {
1649  if(check_self_abilities(*entry.cfg, entry.tag_name)){
1650  return true;
1651  }
1652  }
1653  }
1654 
1655  const auto adjacent = get_adjacent_tiles(self_loc_);
1656  for(unsigned i = 0; i < adjacent.size(); ++i) {
1657  const unit_map::const_iterator it = units.find(adjacent[i]);
1658  if (it == units.end() || it->incapacitated())
1659  continue;
1660  if ( &*it == self_.get() )
1661  continue;
1662 
1663  std::vector<special_match> special_tag_matches_adj;
1664  std::vector<special_match> special_id_matches_adj;
1665  get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1666  if(special_tags){
1667  for(const special_match& entry : special_tag_matches_adj) {
1668  if(check_adj_abilities(*entry.cfg, entry.tag_name, i , *it)){
1669  return true;
1670  }
1671  }
1672  }
1673  if(special_id){
1674  for(const special_match& entry : special_id_matches_adj) {
1675  if(check_adj_abilities(*entry.cfg, entry.tag_name, i , *it)){
1676  return true;
1677  }
1678  }
1679  }
1680  }
1681  }
1682 
1683  if(other_){
1684  std::vector<special_match> special_tag_matches_other;
1685  std::vector<special_match> special_id_matches_other;
1686  get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1687  if(special_tags){
1688  for(const special_match& entry : special_tag_matches_other) {
1689  if(check_self_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, other_loc_, AFFECT_OTHER, entry.tag_name)){
1690  return true;
1691  }
1692  }
1693  }
1694 
1695  if(special_id){
1696  for(const special_match& entry : special_id_matches_other) {
1697  if(check_self_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, other_loc_, AFFECT_OTHER, entry.tag_name)){
1698  return true;
1699  }
1700  }
1701  }
1702 
1703  const auto adjacent = get_adjacent_tiles(other_loc_);
1704  for(unsigned i = 0; i < adjacent.size(); ++i) {
1705  const unit_map::const_iterator it = units.find(adjacent[i]);
1706  if (it == units.end() || it->incapacitated())
1707  continue;
1708  if ( &*it == other_.get() )
1709  continue;
1710 
1711  std::vector<special_match> special_tag_matches_oadj;
1712  std::vector<special_match> special_id_matches_oadj;
1713  get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1714  if(special_tags){
1715  for(const special_match& entry : special_tag_matches_oadj) {
1716  if(check_adj_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *it, i, other_loc_, AFFECT_OTHER, entry.tag_name)){
1717  return true;
1718  }
1719  }
1720  }
1721 
1722  if(special_id){
1723  for(const special_match& entry : special_id_matches_oadj) {
1724  if(check_adj_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *it, i, other_loc_, AFFECT_OTHER, entry.tag_name)){
1725  return true;
1726  }
1727  }
1728  }
1729  }
1730  }
1731  return false;
1732 }
1733 
1734 bool attack_type::has_special_or_ability(const std::string& special, bool special_id, bool special_tags) const
1735 {
1736  //Now that filter_(second)attack in event supports special_id/type_active, including abilities used as weapons,
1737  //these can be detected even in placeholder attacks generated to compensate for the lack of attack in defense against an attacker using a range attack not possessed by the defender.
1738  //It is therefore necessary to check if the range is not empty (proof that the weapon is not a placeholder) to decide if has_weapon_ability can be returned or not.
1739  if(range().empty()){
1740  return false;
1741  }
1742  return (has_special(special, false, special_id, special_tags) || has_weapon_ability(special, special_id, special_tags));
1743 }
1744 //end of emulate weapon special functions.
1745 
1746 bool attack_type::special_active(const config& special, AFFECTS whom, const std::string& tag_name,
1747  const std::string& filter_self) const
1748 {
1749  return special_active_impl(shared_from_this(), other_attack_, special, whom, tag_name, filter_self);
1750 }
1751 
1752 /**
1753  * Returns whether or not the given special is active for the specified unit,
1754  * based on the current context (see set_specials_context).
1755  * @param self_attack this unit's attack
1756  * @param other_attack the other unit's attack
1757  * @param special a weapon special WML structure
1758  * @param whom specifies which combatant we care about
1759  * @param tag_name tag name of the special config
1760  * @param filter_self the filter to use
1761  */
1763  const_attack_ptr self_attack,
1764  const_attack_ptr other_attack,
1765  const config& special,
1766  AFFECTS whom,
1767  const std::string& tag_name,
1768  const std::string& filter_self)
1769 {
1770  assert(self_attack || other_attack);
1771  bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
1772  bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
1773  //log_scope("special_active");
1774 
1775 
1776  // Does this affect the specified unit?
1777  if ( whom == AFFECT_SELF ) {
1778  if ( !special_affects_self(special, is_attacker) )
1779  return false;
1780  }
1781  if ( whom == AFFECT_OTHER ) {
1782  if ( !special_affects_opponent(special, is_attacker) )
1783  return false;
1784  }
1785 
1786  // Is this active on attack/defense?
1787  const std::string & active_on = special["active_on"];
1788  if ( !active_on.empty() ) {
1789  if ( is_attacker && active_on != "offense" )
1790  return false;
1791  if ( !is_attacker && active_on != "defense" )
1792  return false;
1793  }
1794 
1795  // Get the units involved.
1796  const unit_map& units = get_unit_map();
1797 
1798  unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
1799  unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
1800  map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
1801  map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
1802  //TODO: why is this needed?
1803  if(self == nullptr) {
1804  unit_map::const_iterator it = units.find(self_loc);
1805  if(it.valid()) {
1806  self = it.get_shared_ptr();
1807  }
1808  }
1809  if(other == nullptr) {
1810  unit_map::const_iterator it = units.find(other_loc);
1811  if(it.valid()) {
1812  other = it.get_shared_ptr();
1813  }
1814  }
1815 
1816  // Make sure they're facing each other.
1817  temporary_facing self_facing(self, self_loc.get_relative_dir(other_loc));
1818  temporary_facing other_facing(other, other_loc.get_relative_dir(self_loc));
1819 
1820  // Filter poison, plague, drain, slow, petrifies
1821  // True if "whom" corresponds to "self", false if "whom" is "other"
1822  bool whom_is_self = ((whom == AFFECT_SELF) || ((whom == AFFECT_EITHER) && special_affects_self(special, is_attacker)));
1823  unit_const_ptr them = whom_is_self ? other : self;
1824  map_location their_loc = whom_is_self ? other_loc : self_loc;
1825 
1826  if (tag_name == "drains" && them && them->get_state("undrainable")) {
1827  return false;
1828  }
1829  if (tag_name == "plague" && them &&
1830  (them->get_state("unplagueable") ||
1831  resources::gameboard->map().is_village(their_loc))) {
1832  return false;
1833  }
1834  if (tag_name == "poison" && them &&
1835  (them->get_state("unpoisonable") || them->get_state(unit::STATE_POISONED))) {
1836  return false;
1837  }
1838  if (tag_name == "slow" && them &&
1839  (them->get_state("unslowable") || them->get_state(unit::STATE_SLOWED))) {
1840  return false;
1841  }
1842  if (tag_name == "petrifies" && them &&
1843  them->get_state("unpetrifiable")) {
1844  return false;
1845  }
1846 
1847 
1848  // Translate our context into terms of "attacker" and "defender".
1849  unit_const_ptr & att = is_attacker ? self : other;
1850  unit_const_ptr & def = is_attacker ? other : self;
1851  const map_location & att_loc = is_attacker ? self_loc : other_loc;
1852  const map_location & def_loc = is_attacker ? other_loc : self_loc;
1853  const_attack_ptr att_weapon = is_attacker ? self_attack : other_attack;
1854  const_attack_ptr def_weapon = is_attacker ? other_attack : self_attack;
1855 
1856  // Filter firststrike here, if both units have first strike then the effects cancel out. Only check
1857  // the opponent if "whom" is the defender, otherwise this leads to infinite recursion.
1858  if (tag_name == "firststrike") {
1859  bool whom_is_defender = whom_is_self ? !is_attacker : is_attacker;
1860  if (whom_is_defender && att_weapon && att_weapon->has_special_or_ability("firststrike"))
1861  return false;
1862  }
1863 
1864  //Add wml filter if "backstab" attribute used.
1865  if (!special["backstab"].blank()) {
1866  deprecated_message("backstab= in weapon specials", DEP_LEVEL::INDEFINITE, "", "Use [filter_opponent] with a formula instead; the code can be found in data/core/macros/ in the WEAPON_SPECIAL_BACKSTAB macro.");
1867  }
1868  config cfg = special;
1869  if(special["backstab"].to_bool()){
1870  const std::string& backstab_formula = "enemy_of(self, flanker) and not flanker.petrified where flanker = unit_at(direction_from(loc, other.facing))";
1871  config& filter_child = cfg.child_or_add("filter_opponent");
1872  if(!special.has_child("filter_opponent")){
1873  filter_child["formula"] = backstab_formula;
1874  } else {
1875  config filter;
1876  filter["formula"] = backstab_formula;
1877  filter_child.add_child("and", filter);
1878  }
1879  }
1880  const config& special_backstab = special["backstab"].to_bool() ? cfg : special;
1881 
1882  // Filter the units involved.
1883  //If filter concerns the unit on which special is applied,
1884  //then the type of special must be entered to avoid calling
1885  //the function of this special in matches_filter()
1886  std::string self_tag_name = whom_is_self ? tag_name : "";
1887  if (!special_unit_matches(self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_tag_name))
1888  return false;
1889  std::string opp_tag_name = !whom_is_self ? tag_name : "";
1890  if (!special_unit_matches(other, self, other_loc, other_attack, special_backstab, is_for_listing, "filter_opponent", opp_tag_name))
1891  return false;
1892  std::string att_tag_name = is_attacker ? tag_name : "";
1893  if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing, "filter_attacker", att_tag_name))
1894  return false;
1895  std::string def_tag_name = !is_attacker ? tag_name : "";
1896  if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing, "filter_defender", def_tag_name))
1897  return false;
1898 
1899  const auto adjacent = get_adjacent_tiles(self_loc);
1900 
1901  // Filter the adjacent units.
1902  for (const config &i : special.child_range("filter_adjacent"))
1903  {
1904  std::size_t count = 0;
1905  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
1906  unit_filter filter{ vconfig(i) };
1907  for (const map_location::DIRECTION index : dirs)
1908  {
1910  continue;
1911  unit_map::const_iterator unit = units.find(adjacent[index]);
1912  if (unit == units.end() || !filter.matches(*unit, adjacent[index], *self))
1913  return false;
1914  if (i.has_attribute("is_enemy")) {
1916  if (i["is_enemy"].to_bool() != dc.get_team(unit->side()).is_enemy(self->side())) {
1917  continue;
1918  }
1919  }
1920  count++;
1921  }
1922  if (i["count"].empty() && count != dirs.size()) {
1923  return false;
1924  }
1925  if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) {
1926  return false;
1927  }
1928  }
1929 
1930  // Filter the adjacent locations.
1931  for (const config &i : special.child_range("filter_adjacent_location"))
1932  {
1933  std::size_t count = 0;
1934  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
1935  terrain_filter adj_filter(vconfig(i), resources::filter_con, false);
1936  for (const map_location::DIRECTION index : dirs)
1937  {
1939  continue;
1940  if(!adj_filter.match(adjacent[index])) {
1941  return false;
1942  }
1943  count++;
1944  }
1945  if (i["count"].empty() && count != dirs.size()) {
1946  return false;
1947  }
1948  if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) {
1949  return false;
1950  }
1951  }
1952 
1953  return true;
1954 }
1955 
1956 
1957 
1959 {
1960 
1961 void individual_effect::set(value_modifier t, int val, const config *abil, const map_location &l)
1962 {
1963  type=t;
1964  value=val;
1965  ability=abil;
1966  loc=l;
1967 }
1968 
1969 bool filter_base_matches(const config& cfg, int def)
1970 {
1971  if (auto apply_filter = cfg.optional_child("filter_base_value")) {
1972  config::attribute_value cond_eq = apply_filter["equals"];
1973  config::attribute_value cond_ne = apply_filter["not_equals"];
1974  config::attribute_value cond_lt = apply_filter["less_than"];
1975  config::attribute_value cond_gt = apply_filter["greater_than"];
1976  config::attribute_value cond_ge = apply_filter["greater_than_equal_to"];
1977  config::attribute_value cond_le = apply_filter["less_than_equal_to"];
1978  return (cond_eq.empty() || def == cond_eq.to_int()) &&
1979  (cond_ne.empty() || def != cond_ne.to_int()) &&
1980  (cond_lt.empty() || def < cond_lt.to_int()) &&
1981  (cond_gt.empty() || def > cond_gt.to_int()) &&
1982  (cond_ge.empty() || def >= cond_ge.to_int()) &&
1983  (cond_le.empty() || def <= cond_le.to_int());
1984  }
1985  return true;
1986 }
1987 
1988 effect::effect(const unit_ability_list& list, int def, const_attack_ptr att, EFFECTS wham) :
1989  effect_list_(),
1990  composite_value_(0)
1991 {
1992 
1993  int value_set = (wham == EFFECT_CUMULABLE) ? std::max(list.highest("value").first, 0) + std::min(list.lowest("value").first, 0) : def;
1994  std::map<std::string,individual_effect> values_add;
1995  std::map<std::string,individual_effect> values_mul;
1996  std::map<std::string,individual_effect> values_div;
1997 
1998  individual_effect set_effect_max;
1999  individual_effect set_effect_min;
2000  utils::optional<int> max_value = utils::nullopt;
2001  utils::optional<int> min_value = utils::nullopt;
2002 
2003  for (const unit_ability & ability : list) {
2004  const config& cfg = *ability.ability_cfg;
2005  const std::string& effect_id = cfg[cfg["id"].empty() ? "name" : "id"];
2006 
2007  if (!filter_base_matches(cfg, def))
2008  continue;
2009 
2010  if(wham != EFFECT_CUMULABLE){
2011  if (const config::attribute_value *v = cfg.get("value")) {
2012  int value = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
2013  callable.add("base_value", wfl::variant(def));
2014  return formula.evaluate(callable).as_int();
2015  });
2016 
2017  int value_cum = cfg["cumulative"].to_bool() ? std::max(def, value) : value;
2018  assert((set_effect_min.type != NOT_USED) == (set_effect_max.type != NOT_USED));
2019  if(set_effect_min.type == NOT_USED) {
2020  set_effect_min.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2021  set_effect_max.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2022  }
2023  else {
2024  if(value_cum > set_effect_max.value) {
2025  set_effect_max.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2026  }
2027  if(value_cum < set_effect_min.value) {
2028  set_effect_min.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
2029  }
2030  }
2031  }
2032  }
2033 
2034  if(wham == EFFECT_DEFAULT || wham == EFFECT_CUMULABLE){
2035  if(cfg.has_attribute("max_value")){
2036  max_value = max_value ? std::min(*max_value, cfg["max_value"].to_int()) : cfg["max_value"].to_int();
2037  }
2038  if(cfg.has_attribute("min_value")){
2039  min_value = min_value ? std::max(*min_value, cfg["min_value"].to_int()) : cfg["min_value"].to_int();
2040  }
2041  }
2042 
2043  if (const config::attribute_value *v = cfg.get("add")) {
2044  int add = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
2045  callable.add("base_value", wfl::variant(def));
2046  return formula.evaluate(callable).as_int();
2047  });
2048  std::map<std::string,individual_effect>::iterator add_effect = values_add.find(effect_id);
2049  if(add_effect == values_add.end() || add > add_effect->second.value) {
2050  values_add[effect_id].set(ADD, add, ability.ability_cfg, ability.teacher_loc);
2051  }
2052  }
2053  if (const config::attribute_value *v = cfg.get("sub")) {
2054  int sub = - get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
2055  callable.add("base_value", wfl::variant(def));
2056  return formula.evaluate(callable).as_int();
2057  });
2058  std::map<std::string,individual_effect>::iterator sub_effect = values_add.find(effect_id);
2059  if(sub_effect == values_add.end() || sub < sub_effect->second.value) {
2060  values_add[effect_id].set(ADD, sub, ability.ability_cfg, ability.teacher_loc);
2061  }
2062  }
2063  if (const config::attribute_value *v = cfg.get("multiply")) {
2064  int multiply = static_cast<int>(get_single_ability_value(*v, static_cast<double>(def), ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
2065  callable.add("base_value", wfl::variant(def));
2066  return formula.evaluate(callable).as_decimal() / 1000.0 ;
2067  }) * 100);
2068  std::map<std::string,individual_effect>::iterator mul_effect = values_mul.find(effect_id);
2069  if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
2070  values_mul[effect_id].set(MUL, multiply, ability.ability_cfg, ability.teacher_loc);
2071  }
2072  }
2073  if (const config::attribute_value *v = cfg.get("divide")) {
2074  int divide = static_cast<int>(get_single_ability_value(*v, static_cast<double>(def), ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
2075  callable.add("base_value", wfl::variant(def));
2076  return formula.evaluate(callable).as_decimal() / 1000.0 ;
2077  }) * 100);
2078 
2079  if (divide == 0) {
2080  ERR_NG << "division by zero with divide= in ability/weapon special " << effect_id;
2081  }
2082  else {
2083  std::map<std::string,individual_effect>::iterator div_effect = values_div.find(effect_id);
2084  if(div_effect == values_div.end() || divide > div_effect->second.value) {
2085  values_div[effect_id].set(DIV, divide, ability.ability_cfg, ability.teacher_loc);
2086  }
2087  }
2088  }
2089  }
2090 
2091  if((wham != EFFECT_CUMULABLE) && set_effect_max.type != NOT_USED) {
2092  value_set = std::max(set_effect_max.value, 0) + std::min(set_effect_min.value, 0);
2093  if(set_effect_max.value > def) {
2094  effect_list_.push_back(set_effect_max);
2095  }
2096  if(set_effect_min.value < def) {
2097  effect_list_.push_back(set_effect_min);
2098  }
2099  }
2100 
2101  /* Do multiplication with floating point values rather than integers
2102  * We want two places of precision for each multiplier
2103  * Using integers multiplied by 100 to keep precision causes overflow
2104  * after 3-4 abilities for 32-bit values and ~8 for 64-bit
2105  * Avoiding the overflow by dividing after each step introduces rounding errors
2106  * that may vary depending on the order effects are applied
2107  * As the final values are likely <1000 (always true for mainline), loss of less significant digits is not an issue
2108  */
2109  double multiplier = 1.0;
2110  double divisor = 1.0;
2111 
2112  for(const auto& val : values_mul) {
2113  multiplier *= val.second.value/100.0;
2114  effect_list_.push_back(val.second);
2115  }
2116 
2117  for(const auto& val : values_div) {
2118  divisor *= val.second.value/100.0;
2119  effect_list_.push_back(val.second);
2120  }
2121 
2122  int addition = 0;
2123  for(const auto& val : values_add) {
2124  addition += val.second.value;
2125  effect_list_.push_back(val.second);
2126  }
2127 
2128  composite_value_ = static_cast<int>((value_set + addition) * multiplier / divisor);
2129  //clamp what if min_value < max_value or one attribute only used.
2130  if(max_value && min_value && *min_value < *max_value) {
2131  composite_value_ = std::clamp(*min_value, *max_value, composite_value_);
2132  } else if(max_value && !min_value) {
2133  composite_value_ = std::min(*max_value, composite_value_);
2134  } else if(min_value && !max_value) {
2135  composite_value_ = std::max(*min_value, composite_value_);
2136  }
2137 }
2138 
2139 } // end namespace unit_abilities
static std::string select_replacement_type(const unit_ability_list &damage_type_list)
Definition: abilities.cpp:1171
static void add_name(std::string &temp_string, bool active, const std::string name, std::set< std::string > &checking_name)
Definition: abilities.cpp:893
static std::string select_alternative_type(const unit_ability_list &damage_type_list, unit_ability_list resistance_list, const unit &u)
Definition: abilities.cpp:1200
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: abilities.cpp:47
static void add_string_to_vector(std::vector< std::string > &image_list, const config &cfg, const std::string &attribute_name)
Definition: abilities.cpp:479
#define ERR_WML
Definition: abilities.cpp:50
static bool overwrite_special_affects(const config &special)
Definition: abilities.cpp:1447
static void add_name_list(std::string &temp_string, std::string &weapon_abilities, std::set< std::string > &checking_name, const std::string from_str)
Definition: abilities.cpp:943
static lg::log_domain log_wml("wml")
static void get_ability_children(std::vector< special_match > &tag_result, std::vector< special_match > &id_result, const config &parent, const std::string &id, bool special_id=true, bool special_tags=true)
Gets the children of parent (which should be the abilities for an attack_type) and places the ones wh...
Definition: abilities.cpp:1554
double t
Definition: astarsearch.cpp:63
specials_context_t(const attack_type &weapon, bool attacking)
Initialize weapon specials context for listing.
Definition: abilities.cpp:1118
map_location other_loc_
std::string weapon_specials() const
Returns a comma-separated string of active names for the specials of *this.
Definition: abilities.cpp:911
bool check_self_abilities(const config &cfg, const std::string &special) const
check_self_abilities : return an boolean value for checking of activities of abilities used like weap...
Definition: abilities.cpp:1588
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
static bool special_active_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config &special, AFFECTS whom, const std::string &tag_name, const std::string &filter_self="filter_self")
Returns whether or not the given special is active for the specified unit, based on the current conte...
Definition: abilities.cpp:1762
int composite_value(const unit_ability_list &abil_list, int base_value) const
Return the special weapon value, considering specials.
Definition: abilities.cpp:1442
const_attack_ptr other_attack_
void add_formula_context(wfl::map_formula_callable &) const
Definition: abilities.cpp:527
const std::string & range() const
Definition: attack_type.hpp:45
map_location self_loc_
const std::string & type() const
Definition: attack_type.hpp:43
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
Definition: abilities.cpp:1406
std::string weapon_specials_value(const std::set< std::string > checking_tags) const
Definition: abilities.cpp:954
unit_ability_list get_specials_and_abilities(const std::string &special) const
Definition: abilities.cpp:1425
bool overwrite_special_checking(unit_ability_list &overwriters, const config &cfg, const std::string &tag_name) const
Check whether cfg would be overwritten by any element of overwriters.
Definition: abilities.cpp:1489
unit_const_ptr self_
bool check_adj_abilities(const config &cfg, const std::string &special, int dir, const unit &from) const
check_adj_abilities : return an boolean value for checking of activities of abilities used like weapo...
Definition: abilities.cpp:1608
config specials_
int num_attacks() const
Definition: attack_type.hpp:52
unit_ability_list overwrite_special_overwriter(unit_ability_list overwriters, const std::string &tag_name) const
Filter a list of abilities or weapon specials, removing any entries that don't own the overwrite_spec...
Definition: abilities.cpp:1453
static bool check_adj_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config &special, unit_const_ptr u, const unit &from, int dir, const map_location &loc, AFFECTS whom, const std::string &tag_name, bool leader_bool=false)
check_adj_abilities_impl : return an boolean value for checking of activities of abilities used like ...
Definition: abilities.cpp:1613
static void weapon_specials_impl_adj(std::string &temp_string, unit_const_ptr self, const_attack_ptr self_attack, const_attack_ptr other_attack, const map_location &self_loc, AFFECTS whom, std::set< std::string > &checking_name, const std::set< std::string > &checking_tags={}, const std::string &affect_adjacents="", bool leader_bool=false)
Definition: abilities.cpp:1014
std::vector< std::pair< t_string, t_string > > special_tooltips(boost::dynamic_bitset<> *active_list=nullptr) const
Returns a vector of names and descriptions for the specials of *this.
Definition: abilities.cpp:857
static void weapon_specials_impl_self(std::string &temp_string, unit_const_ptr self, const_attack_ptr self_attack, const_attack_ptr other_attack, const map_location &self_loc, AFFECTS whom, std::set< std::string > &checking_name, const std::set< std::string > &checking_tags={}, bool leader_bool=false)
weapon_specials_impl_self and weapon_specials_impl_adj : check if special name can be added.
Definition: abilities.cpp:994
const t_string & name() const
Definition: attack_type.hpp:41
std::set< std::string > alternative_damage_types() const
Definition: abilities.cpp:1265
bool attack_empty() const
Returns true if this is a dummy attack_type, for example the placeholder that the unit_attack dialog ...
static bool check_self_abilities_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config &special, unit_const_ptr u, const map_location &loc, AFFECTS whom, const std::string &tag_name, bool leader_bool=false)
check_self_abilities_impl : return an boolean value for checking of activities of abilities used like...
Definition: abilities.cpp:1593
int modified_damage() const
Returns the damage per attack of this weapon, considering specials.
Definition: abilities.cpp:1286
void modified_attacks(unsigned &min_attacks, unsigned &max_attacks) const
Calculates the number of attacks this weapon has, considering specials.
Definition: abilities.cpp:1150
unit_const_ptr other_
bool special_active(const config &special, AFFECTS whom, const std::string &tag_name, const std::string &filter_self="filter_self") const
Definition: abilities.cpp:1746
unit_ability_list get_specials(const std::string &special) const
Returns the currently active specials as an ability list, given the current context (see set_specials...
Definition: abilities.cpp:824
std::string select_damage_type(const unit_ability_list &damage_type_list, const std::string &key_name, unit_ability_list resistance_list) const
Select best damage type based on frequency count for replacement_type and based on highest damage for...
Definition: abilities.cpp:1228
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:1734
bool has_weapon_ability(const std::string &special, bool special_id=true, bool special_tags=true) const
used for abilities used like weapon
Definition: abilities.cpp:1633
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:1242
bool is_for_listing_
Variant for storing WML attributes.
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
bool blank() const
Tests for an attribute that was never set.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const attribute_value & get_or(const config_key_type key, const config_key_type default_key) const
Chooses a value.
Definition: config.cpp:693
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:367
bool matches(const config &filter) const
Definition: config.cpp:1198
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:317
bool has_attribute(config_key_type key) const
Definition: config.cpp:155
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:887
child_itors child_range(config_key_type key)
Definition: config.cpp:273
config & child_or_add(config_key_type key)
Returns a reference to the first child with the given key.
Definition: config.cpp:406
bool empty() const
Definition: config.cpp:852
const attribute_value * get(config_key_type key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:687
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
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
const team & get_team(int side) const
This getter takes a 1-based side number, not a 0-based team number.
const unit_map & get_units() const
Definition: display.hpp:149
const display_context & get_disp_context() const
Definition: display.hpp:190
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:102
virtual const display_context & get_disp_context() const =0
team & get_team(int i)
Definition: game_board.hpp:92
virtual const unit_map & units() const override
Definition: game_board.hpp:107
virtual const gamemap & map() const override
Definition: game_board.hpp:97
bool is_village(const map_location &loc) const
Definition: map.cpp:65
bool empty() const
Definition: tstring.hpp:186
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
bool is_enemy(int n) const
Definition: team.hpp:229
int get_composite_value() const
Definition: abilities.hpp:49
std::vector< individual_effect > effect_list_
Definition: abilities.hpp:56
void append(const unit_ability_list &other)
Appends the abilities from other to this, ignores other.loc()
Definition: unit.hpp:106
std::pair< int, map_location > lowest(const std::string &key, int def=0) const
Definition: unit.hpp:72
std::vector< unit_ability > cfgs_
Definition: unit.hpp:125
const map_location & loc() const
Definition: unit.hpp:103
void emplace_back(T &&... args)
Definition: unit.hpp:101
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: unit.hpp:68
std::size_t size()
Definition: unit.hpp:95
bool empty() const
Definition: unit.hpp:90
void append_if(const unit_ability_list &other, const Predicate &predicate)
Appends any abilities from other for which the given condition returns true to this,...
Definition: unit.hpp:118
std::pair< int, map_location > get_extremum(const std::string &key, int def, const TComp &comp) const
Definition: abilities.cpp:603
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
Definition: filter.hpp:123
unit_filter & set_use_flat_tod(bool value)
Definition: filter.hpp:113
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
unit_ptr find_unit_ptr(const T &val)
Definition: map.hpp:387
unit_iterator find(std::size_t id)
Definition: map.cpp:302
A single unit type that the player may recruit.
Definition: types.hpp:43
This class represents a single unit of a specific type.
Definition: unit.hpp:133
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
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
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:253
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:29
map_display and display: classes which take care of displaying the map and game-data on the screen.
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
#define UNUSED(x)
Definition: global.hpp:31
bool get_self_ability_bool_weapon(const config &special, const std::string &tag_name, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Checks whether this unit currently possesses a given ability of leadership type.
Definition: abilities.cpp:1578
bool get_adj_ability_bool_weapon(const config &special, const std::string &tag_name, int dir, const map_location &loc, const unit &from, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Checks whether this unit is affected by a given ability of leadership type.
Definition: abilities.cpp:1583
bool ability_active(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability is active.
Definition: abilities.cpp:352
bool ability_affects_self(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability affects the owning unit.
Definition: abilities.cpp:451
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses or is affected by a given ability.
Definition: abilities.cpp:180
bool ability_affects_adjacent(const std::string &ability, const config &cfg, int dir, const map_location &loc, const unit &from) const
Check if an ability affects adjacent units.
Definition: abilities.cpp:424
std::vector< std::tuple< std::string, t_string, t_string, t_string > > ability_tooltips() const
Gets the names and descriptions of this unit's abilities.
Definition: abilities.cpp:324
config abilities_
Definition: unit.hpp:2042
unit_ability_list get_abilities_weapons(const std::string &tag_name, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Definition: abilities.cpp:258
int side_
Definition: unit.hpp:1978
bool ability_affects_weapon(const config &cfg, const_attack_ptr weapon, bool is_opp) const
filters the weapons that condition the use of abilities for combat ([resistance],[leadership] or abil...
Definition: abilities.cpp:459
bool has_ability_type(const std::string &ability) const
Check if the unit has an ability of a specific type.
Definition: abilities.cpp:472
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Gets the unit's active abilities of a particular type if it were on a specified location.
Definition: abilities.cpp:218
bool get_adj_ability_bool(const config &special, const std::string &tag_name, int dir, const map_location &loc, const unit &from) const
Checks whether this unit is affected by a given ability used like weapon.
Definition: abilities.cpp:1572
unit_race::GENDER gender_
Definition: unit.hpp:1980
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
Definition: abilities.cpp:267
map_location loc_
Definition: unit.hpp:1941
bool get_self_ability_bool(const config &special, const std::string &tag_name, const map_location &loc) const
Checks whether this unit currently possesses a given ability used like weapon.
Definition: abilities.cpp:1567
const std::string & id() const
Gets this unit's id.
Definition: unit.hpp:380
int side() const
The side this unit belongs to.
Definition: unit.hpp:343
@ STATE_SLOWED
Definition: unit.hpp:860
@ STATE_POISONED
The unit is slowed - it moves slower and does less damage.
Definition: unit.hpp:861
int resistance_value(unit_ability_list resistance_list, const std::string &damage_name) const
For the provided list of resistance abilities, determine the damage resistance based on which are act...
Definition: unit.cpp:1835
const std::vector< std::string > halo_or_icon_abilities(const std::string &image_type) const
Definition: abilities.cpp:487
New lexcical_cast header.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:474
Standard logging facilities (interface).
CURSOR_TYPE get()
Definition: cursor.cpp:216
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:176
const color_t inactive_details_color
const color_t BUTTON_COLOR
std::string span_color(const color_t &color)
Returns a Pango formatting string using the provided color_t object.
static bool is_active(const widget *wgt)
Definition: window.cpp:1281
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:554
game_board * gameboard
Definition: resources.cpp:20
filter_context * filter_con
Definition: resources.cpp:23
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:1969
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
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...
void erase_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::remove_if on a container.
Definition: general.hpp:103
void sort_if(Container &container, const Predicate &predicate)
Convenience wrapper for using std::sort on a container.
Definition: general.hpp:113
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:34
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
const config::attribute_value & gender_value(const config &cfg, unit_race::GENDER gender, const std::string &male_key, const std::string &female_key, const std::string &default_key)
Chooses a value from the given config based on gender.
Definition: race.cpp:159
const child_map::key_type & key
Definition: config.hpp:684
config & cfg
Definition: config.hpp:685
Encapsulates the map of the game.
Definition: location.hpp:38
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
static std::vector< DIRECTION > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
Definition: location.cpp:124
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:226
bool valid() const
Definition: location.hpp:89
static const map_location & null_location()
Definition: location.hpp:81
void set(value_modifier t, int val, const config *abil, const map_location &l)
Definition: abilities.cpp:1961
Data typedef for unit_ability_list.
Definition: unit.hpp:38
map_location student_loc
Used by the formula in the ability.
Definition: unit.hpp:52
const config * ability_cfg
The contents of the ability tag, never nullptr.
Definition: unit.hpp:59
map_location teacher_loc
The location of the teacher, that is the unit who owns the ability tags (different from student becau...
Definition: unit.hpp:57
bool valid() const
Definition: map.hpp:273
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:217
mock_char c
mock_party p
static map_location::DIRECTION s
#define d
#define e