The Battle for Wesnoth  1.15.12+dev
abilities.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Dominic Bolin <dominic.bolin@exong.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Manage unit-abilities, like heal, cure, and weapon_specials.
18  */
19 
20 #include "display.hpp"
21 #include "display_context.hpp"
22 #include "font/text_formatting.hpp"
23 #include "game_board.hpp"
24 #include "global.hpp"
25 #include "lexical_cast.hpp"
26 #include "log.hpp"
27 #include "map/map.hpp"
28 #include "resources.hpp"
29 #include "team.hpp"
30 #include "terrain/filter.hpp"
31 #include "units/unit.hpp"
32 #include "units/abilities.hpp"
33 #include "units/filter.hpp"
34 #include "units/map.hpp"
35 #include "filter_context.hpp"
36 #include "formula/callable_objects.hpp"
37 #include "formula/formula.hpp"
39 #include "deprecation.hpp"
40 
41 #include <boost/dynamic_bitset.hpp>
42 
43 #include <string_view>
44 
45 static lg::log_domain log_engine("engine");
46 #define ERR_NG LOG_STREAM(err, log_engine)
47 
48 namespace {
49  class temporary_facing
50  {
51  map_location::DIRECTION save_dir_;
52  unit_const_ptr u_;
53  public:
54  temporary_facing(unit_const_ptr u, map_location::DIRECTION new_dir)
55  : save_dir_(u ? u->facing() : map_location::NDIRECTIONS)
56  , u_(u)
57  {
58  if (u_) {
59  u_->set_facing(new_dir);
60  }
61  }
62  ~temporary_facing()
63  {
64  if (u_) {
65  u_->set_facing(save_dir_);
66  }
67  }
68  };
69 }
70 
71 /*
72  *
73  * [abilities]
74  * ...
75  *
76  * [heals]
77  * value=4
78  * max_value=8
79  * cumulative=no
80  * affect_allies=yes
81  * name= _ "heals"
82  * female_name= _ "female^heals"
83  * name_inactive=null
84  * female_name_inactive=null
85  * description= _ "Heals:
86 Allows the unit to heal adjacent friendly units at the beginning of each turn.
87 
88 A unit cared for by a healer may heal up to 4 HP per turn.
89 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."
90  * description_inactive=null
91  *
92  * affect_self=yes
93  * [filter] // SUF
94  * ...
95  * [/filter]
96  * [filter_self] // SUF
97  * ...
98  * [/filter_self]
99  * [filter_adjacent] // SUF
100  * adjacent=n,ne,nw
101  * ...
102  * [/filter_adjacent]
103  * [filter_adjacent_location]
104  * adjacent=n,ne,nw
105  * ...
106  * [/filter_adjacent]
107  * [affect_adjacent]
108  * adjacent=n,ne,nw
109  * [filter] // SUF
110  * ...
111  * [/filter]
112  * [/affect_adjacent]
113  * [affect_adjacent]
114  * adjacent=s,se,sw
115  * [filter] // SUF
116  * ...
117  * [/filter]
118  * [/affect_adjacent]
119  *
120  * [/heals]
121  *
122  * ...
123  * [/abilities]
124  *
125  */
126 
127 
128 namespace {
129 
130 bool affects_side(const config& cfg, std::size_t side, std::size_t other_side)
131 {
132  // display::get_singleton() has already been confirmed valid by both callers.
133  const team& side_team = display::get_singleton()->get_disp_context().get_team(side);
134 
135  if(side == other_side)
136  return cfg["affect_allies"].to_bool(true);
137  if(side_team.is_enemy(other_side))
138  return cfg["affect_enemies"].to_bool();
139  else
140  return cfg["affect_allies"].to_bool();
141 }
142 
143 }
144 
145 bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc) const
146 {
147  for (const config &i : this->abilities_.child_range(tag_name)) {
148  if (ability_active(tag_name, i, loc) &&
149  ability_affects_self(tag_name, i, loc))
150  {
151  return true;
152  }
153  }
154 
155  assert(display::get_singleton());
156  const unit_map& units = display::get_singleton()->get_units();
157 
158  const auto adjacent = get_adjacent_tiles(loc);
159  for(unsigned i = 0; i < adjacent.size(); ++i) {
160  const unit_map::const_iterator it = units.find(adjacent[i]);
161  if (it == units.end() || it->incapacitated())
162  continue;
163  // Abilities may be tested at locations other than the unit's current
164  // location. This is intentional to allow for less messing with the unit
165  // map during calculations, particularly with regards to movement.
166  // Thus, we need to make sure the adjacent unit (*it) is not actually
167  // ourself.
168  if ( &*it == this )
169  continue;
170  for (const config &j : it->abilities_.child_range(tag_name)) {
171  if (affects_side(j, side(), it->side()) &&
172  it->ability_active(tag_name, j, adjacent[i]) &&
173  ability_affects_adjacent(tag_name, j, i, loc, *it))
174  {
175  return true;
176  }
177  }
178  }
179 
180 
181  return false;
182 }
183 
184 unit_ability_list unit::get_abilities(const std::string& tag_name, const map_location& loc) const
185 {
186  unit_ability_list res(loc_);
187 
188  for(const config& i : this->abilities_.child_range(tag_name)) {
189  if(ability_active(tag_name, i, loc)
190  && ability_affects_self(tag_name, i, loc))
191  {
192  res.emplace_back(&i, loc, loc);
193  }
194  }
195 
196  assert(display::get_singleton());
197  const unit_map& units = display::get_singleton()->get_units();
198 
199  const auto adjacent = get_adjacent_tiles(loc);
200  for(unsigned i = 0; i < adjacent.size(); ++i) {
201  const unit_map::const_iterator it = units.find(adjacent[i]);
202  if (it == units.end() || it->incapacitated())
203  continue;
204  // Abilities may be tested at locations other than the unit's current
205  // location. This is intentional to allow for less messing with the unit
206  // map during calculations, particularly with regards to movement.
207  // Thus, we need to make sure the adjacent unit (*it) is not actually
208  // ourself.
209  if ( &*it == this )
210  continue;
211  for(const config& j : it->abilities_.child_range(tag_name)) {
212  if(affects_side(j, side(), it->side())
213  && it->ability_active(tag_name, j, adjacent[i])
214  && ability_affects_adjacent(tag_name, j, i, loc, *it))
215  {
216  res.emplace_back(&j, loc, adjacent[i]);
217  }
218  }
219  }
220 
221 
222  return res;
223 }
224 
225 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
226 {
227  unit_ability_list res = get_abilities(tag_name, loc);
228  for(unit_ability_list::iterator i = res.begin(); i != res.end();) {
229  if((!ability_affects_weapon(*i->ability_cfg, weapon, false) || !ability_affects_weapon(*i->ability_cfg, opp_weapon, true))) {
230  i = res.erase(i);
231  } else {
232  ++i;
233  }
234  }
235  return res;
236 }
237 
238 std::vector<std::string> unit::get_ability_list() const
239 {
240  std::vector<std::string> res;
241 
242  for (const config::any_child &ab : this->abilities_.all_children_range()) {
243  std::string id = ab.cfg["id"];
244  if (!id.empty())
245  res.push_back(std::move(id));
246  }
247  return res;
248 }
249 
250 
251 namespace {
252  // These functions might have wider usefulness than this file, but for now
253  // I'll make them local.
254 
255  /**
256  * Chooses a value from the given config. If the value specified by @a key is
257  * blank, then @a default_key is chosen instead.
258  */
259  inline const config::attribute_value & default_value(
260  const config & cfg, const std::string & key, const std::string & default_key)
261  {
262  const config::attribute_value & value = cfg[key];
263  return !value.blank() ? value : cfg[default_key];
264  }
265 
266  /**
267  * Chooses a value from the given config based on gender. If the value for
268  * the specified gender is blank, then @a default_key is chosen instead.
269  */
270  inline const config::attribute_value & gender_value(
271  const config & cfg, unit_race::GENDER gender, const std::string & male_key,
272  const std::string & female_key, const std::string & default_key)
273  {
274  return default_value(cfg,
275  gender == unit_race::MALE ? male_key : female_key,
276  default_key);
277  }
278 
279  /**
280  * Adds a quadruple consisting of (in order) id, base name,
281  * male or female name as appropriate for the unit, and description.
282  *
283  * @returns Whether name was resolved and quadruple added.
284  */
285  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)
286  {
287  if (active) {
288  const t_string& name = gender_value(ab.cfg, gender, "name", "female_name", "name").t_str();
289 
290  if (!name.empty()) {
291  res.emplace_back(
292  ab.cfg["id"],
293  ab.cfg["name"].t_str(),
294  name,
295  ab.cfg["description"].t_str() );
296  return true;
297  }
298  }
299  else
300  {
301  // See if an inactive name was specified.
302  const config::attribute_value& inactive_value =
303  gender_value(ab.cfg, gender, "name_inactive",
304  "female_name_inactive", "name_inactive");
305  const t_string& name = !inactive_value.blank() ? inactive_value.t_str() :
306  gender_value(ab.cfg, gender, "name", "female_name", "name").t_str();
307 
308  if (!name.empty()) {
309  res.emplace_back(
310  ab.cfg["id"],
311  default_value(ab.cfg, "name_inactive", "name").t_str(),
312  name,
313  default_value(ab.cfg, "description_inactive", "description").t_str() );
314  return true;
315  }
316  }
317 
318  return false;
319  }
320 }
321 
322 std::vector<std::tuple<std::string, t_string, t_string, t_string>> unit::ability_tooltips() const
323 {
324  std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
325 
326  for (const config::any_child &ab : this->abilities_.all_children_range())
327  {
328  add_ability_tooltip(ab, gender_, res, true);
329  }
330 
331  return res;
332 }
333 
334 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
335 {
336  std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
337  active_list.clear();
338 
339  for (const config::any_child &ab : this->abilities_.all_children_range())
340  {
341  bool active = ability_active(ab.key, ab.cfg, loc);
342  if (add_ability_tooltip(ab, gender_, res, active))
343  {
344  active_list.push_back(active);
345  }
346  }
347  return res;
348 }
349 
350 bool unit::ability_active(const std::string& ability,const config& cfg,const map_location& loc) const
351 {
352  bool illuminates = ability == "illuminates";
353 
354  if (const config &afilter = cfg.child("filter"))
355  if ( !unit_filter(vconfig(afilter)).set_use_flat_tod(illuminates).matches(*this, loc) )
356  return false;
357 
358  const auto adjacent = get_adjacent_tiles(loc);
359 
360  assert(display::get_singleton());
361  const unit_map& units = display::get_singleton()->get_units();
362 
363  for (const config &i : cfg.child_range("filter_adjacent"))
364  {
365  std::size_t count = 0;
366  unit_filter ufilt{ vconfig(i) };
367  ufilt.set_use_flat_tod(illuminates);
368  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
369  for (const map_location::DIRECTION index : dirs)
370  {
372  continue;
373  unit_map::const_iterator unit = units.find(adjacent[index]);
374  if (unit == units.end())
375  return false;
376  if (!ufilt(*unit, *this))
377  return false;
378  if (i.has_attribute("is_enemy")) {
380  if (i["is_enemy"].to_bool() != dc.get_team(unit->side()).is_enemy(side_)) {
381  continue;
382  }
383  }
384  count++;
385  }
386  if (i["count"].empty() && count != dirs.size()) {
387  return false;
388  }
389  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
390  return false;
391  }
392  }
393 
394  for (const config &i : cfg.child_range("filter_adjacent_location"))
395  {
396  std::size_t count = 0;
397  terrain_filter adj_filter(vconfig(i), resources::filter_con, false);
398  adj_filter.flatten(illuminates);
399 
400  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
401  for (const map_location::DIRECTION index : dirs)
402  {
404  continue;
405  }
406  if(!adj_filter.match(adjacent[index])) {
407  return false;
408  }
409  count++;
410  }
411  if (i["count"].empty() && count != dirs.size()) {
412  return false;
413  }
414  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
415  return false;
416  }
417  }
418  return true;
419 }
420 
421 bool unit::ability_affects_adjacent(const std::string& ability, const config& cfg,int dir,const map_location& loc,const unit& from) const
422 {
423  bool illuminates = ability == "illuminates";
424 
425  assert(dir >=0 && dir <= 5);
426  map_location::DIRECTION direction = static_cast<map_location::DIRECTION>(dir);
427 
428  for (const config &i : cfg.child_range("affect_adjacent"))
429  {
430  if (i.has_attribute("adjacent")) { //key adjacent defined
431  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
432  if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
433  continue;
434  }
435  }
436  const config &filter = i.child("filter");
437  if (!filter || //filter tag given
438  unit_filter(vconfig(filter)).set_use_flat_tod(illuminates).matches(*this, loc, from) ) {
439  return true;
440  }
441  }
442  return false;
443 }
444 
445 bool unit::ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const
446 {
447  const config &filter = cfg.child("filter_self");
448  bool affect_self = cfg["affect_self"].to_bool(true);
449  if (!filter || !affect_self) return affect_self;
450  return unit_filter(vconfig(filter)).set_use_flat_tod(ability == "illuminates").matches(*this, loc);
451 }
452 
453 bool unit::ability_affects_weapon(const config& cfg, const_attack_ptr weapon, bool is_opp) const
454 {
455  const std::string filter_tag_name = is_opp ? "filter_second_weapon" : "filter_weapon";
456  if(!cfg.has_child(filter_tag_name)) {
457  return true;
458  }
459  const config& filter = cfg.child(filter_tag_name);
460  if(!weapon) {
461  return false;
462  }
463  return weapon->matches_filter(filter);
464 }
465 
466 bool unit::has_ability_type(const std::string& ability) const
467 {
468  return !abilities_.child_range(ability).empty();
469 }
470 
472 {
473  if(unit_const_ptr & att = is_attacker_ ? self_ : other_) {
474  callable.add("attacker", wfl::variant(std::make_shared<wfl::unit_callable>(*att)));
475  }
476  if(unit_const_ptr & def = is_attacker_ ? other_ : self_) {
477  callable.add("defender", wfl::variant(std::make_shared<wfl::unit_callable>(*def)));
478  }
479 }
480 
481 namespace {
482 
483 
484 template<typename T, typename TFuncFormula>
485 class get_ability_value_visitor
486 #ifdef USING_BOOST_VARIANT
487  : public boost::static_visitor<T>
488 #endif
489 {
490 public:
491  // Constructor stores the default value.
492  get_ability_value_visitor(T def, const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
493 
494  T operator()(const utils::monostate&) const { return def_; }
495  T operator()(bool) const { return def_; }
496  T operator()(int i) const { return static_cast<T>(i); }
497  T operator()(unsigned long long u) const { return static_cast<T>(u); }
498  T operator()(double d) const { return static_cast<T>(d); }
499  T operator()(const t_string&) const { return def_; }
500  T operator()(const std::string& s) const
501  {
502  if(s.size() >= 2 && s[0] == '(') {
503  return formula_handler_(s);
504  }
505  return lexical_cast_default<T>(s, def_);
506  }
507 
508 private:
509  const T def_;
510  const TFuncFormula& formula_handler_;
511 };
512 
513 template<typename T, typename TFuncFormula>
514 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)
515 {
516  return v.apply_visitor(get_ability_value_visitor(def, [&](const std::string& s) {
517 
518  try {
519  assert(display::get_singleton());
520  const unit_map& units = display::get_singleton()->get_units();
521 
522  auto u_itor = units.find(ability_info.teacher_loc);
523 
524  if(u_itor == units.end()) {
525  return def;
526  }
527  wfl::map_formula_callable callable(std::make_shared<wfl::unit_callable>(*u_itor));
528  if(att) {
529  att->add_formula_context(callable);
530  }
531  if (auto uptr = units.find_unit_ptr(ability_info.student_loc)) {
532  callable.add("student", wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
533  }
534  if (auto uptr = units.find_unit_ptr(receiver_loc)) {
535  callable.add("other", wfl::variant(std::make_shared<wfl::unit_callable>(*uptr)));
536  }
537  return formula_handler(wfl::formula(s, new wfl::gamestate_function_symbol_table), callable);
538  } catch(const wfl::formula_error& e) {
539  lg::wml_error() << "Formula error in ability or weapon special: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
540  return def;
541  }
542  }));
543 }
544 }
545 
546 template<typename TComp>
547 std::pair<int,map_location> unit_ability_list::get_extremum(const std::string& key, int def, const TComp& comp) const
548 {
549  if ( cfgs_.empty() ) {
550  return std::pair(def, map_location());
551  }
552  // The returned location is the best non-cumulative one, if any,
553  // the best absolute cumulative one otherwise.
554  map_location best_loc;
555  bool only_cumulative = true;
556  int abs_max = 0;
557  int flat = 0;
558  int stack = 0;
559  for (const unit_ability& p : cfgs_)
560  {
561  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) {
562  return formula.evaluate(callable).as_int();
563  });
564 
565  if ((*p.ability_cfg)["cumulative"].to_bool()) {
566  stack += value;
567  if (value < 0) value = -value;
568  if (only_cumulative && !comp(value, abs_max)) {
569  abs_max = value;
570  best_loc = p.teacher_loc;
571  }
572  } else if (only_cumulative || comp(flat, value)) {
573  only_cumulative = false;
574  flat = value;
575  best_loc = p.teacher_loc;
576  }
577  }
578  return std::pair(flat + stack, best_loc);
579 }
580 
581 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;
582 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;
583 
584 /*
585  *
586  * [special]
587  * [swarm]
588  * name= _ "swarm"
589  * name_inactive= _ ""
590  * description= _ ""
591  * description_inactive= _ ""
592  * cumulative=no
593  * apply_to=self #self,opponent,defender,attacker,both
594  * #active_on=defense # or offense; omitting this means "both"
595  *
596  * swarm_attacks_max=4
597  * swarm_attacks_min=2
598  *
599  * [filter_self] // SUF
600  * ...
601  * [/filter_self]
602  * [filter_opponent] // SUF
603  * [filter_attacker] // SUF
604  * [filter_defender] // SUF
605  * [filter_adjacent] // SAUF
606  * [filter_adjacent_location] // SAUF + locs
607  * [/swarm]
608  * [/special]
609  *
610  */
611 
612 namespace {
613 
614  struct special_match
615  {
616  std::string tag_name;
617  const config* cfg;
618  };
619 
620  /**
621  * Gets the children of @parent (which should be the specials for an
622  * attack_type) and places the ones whose tag or id= matches @a id into
623  * @a tag_result and @a id_result.
624  *
625  * If @a just_peeking is set to true, then @a tag_result and @a id_result
626  * are not touched; instead the return value is used to indicate if any
627  * matching children were found.
628  *
629  * @returns true if @a just_peeking is true and a match was found;
630  * false otherwise.
631  */
632  bool get_special_children(std::vector<special_match>& tag_result,
633  std::vector<special_match>& id_result,
634  const config& parent, const std::string& id,
635  bool just_peeking=false) {
636  for (const config::any_child &sp : parent.all_children_range())
637  {
638  if (just_peeking && (sp.key == id || sp.cfg["id"] == id)) {
639  return true; // peek succeeded; done
640  }
641 
642  if(sp.key == id) {
643  special_match special = { sp.key, &sp.cfg };
644  tag_result.push_back(special);
645  }
646  if(sp.cfg["id"] == id) {
647  special_match special = { sp.key, &sp.cfg };
648  id_result.push_back(special);
649  }
650  }
651  return false;
652  }
653 
654  bool get_special_children_id(std::vector<special_match>& id_result,
655  const config& parent, const std::string& id,
656  bool just_peeking=false) {
657  for (const config::any_child &sp : parent.all_children_range())
658  {
659  if (just_peeking && (sp.cfg["id"] == id)) {
660  return true; // peek succeeded; done
661  }
662 
663  if(sp.cfg["id"] == id) {
664  special_match special = { sp.key, &sp.cfg };
665  id_result.push_back(special);
666  }
667  }
668  return false;
669  }
670 
671  bool get_special_children_tags(std::vector<special_match>& tag_result,
672  const config& parent, const std::string& id,
673  bool just_peeking=false) {
674  for (const config::any_child &sp : parent.all_children_range())
675  {
676  if (just_peeking && (sp.key == id)) {
677  return true; // peek succeeded; done
678  }
679 
680  if(sp.key == id) {
681  special_match special = { sp.key, &sp.cfg };
682  tag_result.push_back(special);
683  }
684  }
685  return false;
686  }
687 }
688 
689 /**
690  * Returns whether or not @a *this has a special with a tag or id equal to
691  * @a special. If @a simple_check is set to true, then the check is merely
692  * for being present. Otherwise (the default), the check is for a special
693  * active in the current context (see set_specials_context), including
694  * specials obtained from the opponent's attack.
695  */
696 bool attack_type::has_special(const std::string& special, bool simple_check, bool special_id, bool special_tags) const
697 {
698  {
699  std::vector<special_match> special_tag_matches;
700  std::vector<special_match> special_id_matches;
701  if(special_id && special_tags){
702  if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
703  return true;
704  }
705  } else if(special_id && !special_tags){
706  if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
707  return true;
708  }
709  } else if(!special_id && special_tags){
710  if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
711  return true;
712  }
713  }
714  // If we make it to here, then either list.empty() or !simple_check.
715  // So if the list is not empty, then this is not a simple check and
716  // we need to check each special in the list to see if any are active.
717  if(special_tags){
718  for(const special_match& entry : special_tag_matches) {
719  if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
720  return true;
721  }
722  }
723  }
724  if(special_id){
725  for(const special_match& entry : special_id_matches) {
726  if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
727  return true;
728  }
729  }
730  }
731  }
732 
733  // Skip checking the opponent's attack?
734  if ( simple_check || !other_attack_ ) {
735  return false;
736  }
737 
738  std::vector<special_match> special_tag_matches;
739  std::vector<special_match> special_id_matches;
740  if(special_id && special_tags){
741  get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
742  } else if(special_id && !special_tags){
743  get_special_children_id(special_id_matches, other_attack_->specials_, special);
744  } else if(!special_id && special_tags){
745  get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
746  }
747  if(special_tags){
748  for(const special_match& entry : special_tag_matches) {
749  if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
750  return true;
751  }
752  }
753  }
754  if(special_id){
755  for(const special_match& entry : special_id_matches) {
756  if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
757  return true;
758  }
759  }
760  }
761  return false;
762 }
763 
764 /**
765  * Returns the currently active specials as an ability list, given the current
766  * context (see set_specials_context).
767  */
768 unit_ability_list attack_type::get_specials(const std::string& special) const
769 {
770  //log_scope("get_specials");
771  const map_location loc = self_ ? self_->get_location() : self_loc_;
772  unit_ability_list res(loc);
773 
774  for(const config& i : specials_.child_range(special)) {
775  if(special_active(i, AFFECT_SELF, special)) {
776  res.emplace_back(&i, loc, loc);
777  }
778  }
779 
780  if(!other_attack_) {
781  return res;
782  }
783 
784  for(const config& i : other_attack_->specials_.child_range(special)) {
785  if(other_attack_->special_active(i, AFFECT_OTHER, special)) {
786  res.emplace_back(&i, other_loc_, other_loc_);
787  }
788  }
789  return res;
790 }
791 
792 /**
793  * Returns a vector of names and descriptions for the specials of *this.
794  * Each std::pair in the vector has first = name and second = description.
795  *
796  * This uses either the active or inactive name/description for each special,
797  * based on the current context (see set_specials_context), provided
798  * @a active_list is not nullptr. Otherwise specials are assumed active.
799  * If the appropriate name is empty, the special is skipped.
800  */
801 std::vector<std::pair<t_string, t_string>> attack_type::special_tooltips(
802  boost::dynamic_bitset<>* active_list) const
803 {
804  //log_scope("special_tooltips");
805  std::vector<std::pair<t_string, t_string>> res;
806  if ( active_list )
807  active_list->clear();
808 
809  for (const config::any_child &sp : specials_.all_children_range())
810  {
811  if ( !active_list || special_active(sp.cfg, AFFECT_EITHER, sp.key) ) {
812  const t_string &name = sp.cfg["name"];
813  if (!name.empty()) {
814  res.emplace_back(name, sp.cfg["description"].t_str() );
815  if ( active_list )
816  active_list->push_back(true);
817  }
818  } else {
819  const t_string& name = default_value(sp.cfg, "name_inactive", "name").t_str();
820  if (!name.empty()) {
821  res.emplace_back(name, default_value(sp.cfg, "description_inactive", "description").t_str() );
822  active_list->push_back(false);
823  }
824  }
825  }
826  return res;
827 }
828 
829 /**
830  * Returns a comma-separated string of active names for the specials of *this.
831  * Empty names are skipped.
832  *
833  * This excludes inactive specials if only_active is true. Whether or not a
834  * special is active depends on the current context (see set_specials_context)
835  * and the @a is_backstab parameter.
836  */
837 std::string attack_type::weapon_specials(bool only_active, bool is_backstab) const
838 {
839  //log_scope("weapon_specials");
840  std::string res;
841  for (const config::any_child &sp : specials_.all_children_range())
842  {
843  const bool active = special_active(sp.cfg, AFFECT_EITHER, sp.key, is_backstab);
844 
845  const std::string& name =
846  active
847  ? sp.cfg["name"].str()
848  : default_value(sp.cfg, "name_inactive", "name").str();
849  if (!name.empty()) {
850  if (!res.empty()) res += ", ";
851  if (only_active && !active) res += font::span_color(font::inactive_details_color);
852  res += name;
853  if (only_active && !active) res += "</span>";
854  }
855  }
856 
857  return res;
858 }
859 
860 
861 /**
862  * Sets the context under which specials will be checked for being active.
863  * This version is appropriate if both units in a combat are known.
864  * @param[in] weapon The weapon being considered.
865  * @param[in] self A reference to the unit with this weapon.
866  * @param[in] other A reference to the other unit in the combat.
867  * @param[in] unit_loc The location of the unit with this weapon.
868  * @param[in] other_loc The location of the other unit in the combat.
869  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
870  * @param[in] other_attack The attack used by the other unit.
871  */
873  const_attack_ptr other_attack,
874  unit_const_ptr self,
875  unit_const_ptr other,
876  const map_location& unit_loc,
877  const map_location& other_loc,
878  bool attacking)
879  : parent(weapon.shared_from_this())
880 {
881  weapon.self_ = self;
882  weapon.other_ = other;
883  weapon.self_loc_ = unit_loc;
884  weapon.other_loc_ = other_loc;
885  weapon.is_attacker_ = attacking;
886  weapon.other_attack_ = other_attack;
887  weapon.is_for_listing_ = false;
888 }
889 
890 /**
891  * Sets the context under which specials will be checked for being active.
892  * This version is appropriate if there is no specific combat being considered.
893  * @param[in] weapon The weapon being considered.
894  * @param[in] self A reference to the unit with this weapon.
895  * @param[in] loc The location of the unit with this weapon.
896  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
897  */
899  : parent(weapon.shared_from_this())
900 {
901  weapon.self_ = self;
902  weapon.other_ = unit_ptr();
903  weapon.self_loc_ = loc;
905  weapon.is_attacker_ = attacking;
906  weapon.other_attack_ = nullptr;
907  weapon.is_for_listing_ = false;
908 }
909 
910 /**
911  * Sets the context under which specials will be checked for being active.
912  * This version is appropriate for theoretical units of a particular type.
913  * @param[in] weapon The weapon being considered.
914  * @param[in] self_type A reference to the type of the unit with this weapon.
915  * @param[in] loc The location of the unit with this weapon.
916  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
917  */
918 attack_type::specials_context_t::specials_context_t(const attack_type& weapon, const unit_type& self_type, const map_location& loc, bool attacking)
919  : parent(weapon.shared_from_this())
920 {
921  UNUSED(self_type);
922  weapon.self_ = unit_ptr();
923  weapon.other_ = unit_ptr();
924  weapon.self_loc_ = loc;
926  weapon.is_attacker_ = attacking;
927  weapon.other_attack_ = nullptr;
928  weapon.is_for_listing_ = false;
929 }
930 
932  : parent(weapon.shared_from_this())
933 {
934  weapon.is_for_listing_ = true;
935  weapon.is_attacker_ = attacking;
936 }
937 
939 {
940  if(was_moved) return;
941  parent->self_ = unit_ptr();
942  parent->other_ = unit_ptr();
943  parent->self_loc_ = map_location::null_location();
944  parent->other_loc_ = map_location::null_location();
945  parent->is_attacker_ = false;
946  parent->other_attack_ = nullptr;
947  parent->is_for_listing_ = false;
948 }
949 
951  : parent(other.parent)
952 {
953  other.was_moved = true;
954 }
955 
956 /**
957  * Calculates the number of attacks this weapon has, considering specials.
958  * This returns two numbers because of the swarm special. The actual number of
959  * attacks depends on the unit's health and should be:
960  * min_attacks + (max_attacks - min_attacks) * (current hp) / (max hp)
961  * c.f. swarm_blows()
962  */
963 void attack_type::modified_attacks(bool is_backstab, unsigned & min_attacks,
964  unsigned & max_attacks) const
965 {
966  // Apply [attacks].
967  unit_abilities::effect attacks_effect(get_specials_and_abilities("attacks"),
968  num_attacks(), is_backstab, shared_from_this());
969  int attacks_value = attacks_effect.get_composite_value();
970 
971  if ( attacks_value < 0 ) {
972  attacks_value = num_attacks();
973  ERR_NG << "negative number of strikes after applying weapon specials" << std::endl;
974  }
975 
976  // Apply [swarm].
977  unit_ability_list swarm_specials = get_specials_and_abilities("swarm");
978  if ( !swarm_specials.empty() ) {
979  min_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_min").first);
980  max_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_max", attacks_value).first);
981  } else {
982  min_attacks = max_attacks = attacks_value;
983  }
984 }
985 
986 
987 /**
988  * Returns the damage per attack of this weapon, considering specials.
989  */
990 int attack_type::modified_damage(bool is_backstab) const
991 {
992  unit_abilities::effect dmg_effect(get_specials_and_abilities("damage"), damage(), is_backstab, shared_from_this());
993  int damage_value = dmg_effect.get_composite_value();
994  return damage_value;
995 }
996 
997 
998 namespace { // Helpers for attack_type::special_active()
999 
1000  /**
1001  * Returns whether or not the given special affects the opponent of the unit
1002  * with the special.
1003  * @param[in] special a weapon special WML structure
1004  * @param[in] is_attacker whether or not the unit with the special is the attacker
1005  */
1006  bool special_affects_opponent(const config& special, bool is_attacker)
1007  {
1008  //log_scope("special_affects_opponent");
1009  const std::string& apply_to = special["apply_to"];
1010  if ( apply_to.empty() )
1011  return false;
1012  if ( apply_to == "both" )
1013  return true;
1014  if ( apply_to == "opponent" )
1015  return true;
1016  if ( is_attacker && apply_to == "defender" )
1017  return true;
1018  if ( !is_attacker && apply_to == "attacker" )
1019  return true;
1020  return false;
1021  }
1022 
1023  /**
1024  * Returns whether or not the given special affects the unit with the special.
1025  * @param[in] special a weapon special WML structure
1026  * @param[in] is_attacker whether or not the unit with the special is the attacker
1027  */
1028  bool special_affects_self(const config& special, bool is_attacker)
1029  {
1030  //log_scope("special_affects_self");
1031  const std::string& apply_to = special["apply_to"];
1032  if ( apply_to.empty() )
1033  return true;
1034  if ( apply_to == "both" )
1035  return true;
1036  if ( apply_to == "self" )
1037  return true;
1038  if ( is_attacker && apply_to == "attacker" )
1039  return true;
1040  if ( !is_attacker && apply_to == "defender")
1041  return true;
1042  return false;
1043  }
1044 
1045  /**
1046  * Determines if a unit/weapon combination matches the specified child
1047  * (normally a [filter_*] child) of the provided filter.
1048  * @param[in] u A unit to filter.
1049  * @param[in] u2 Another unit to filter.
1050  * @param[in] loc The presumed location of @a un_it.
1051  * @param[in] weapon The attack_type to filter.
1052  * @param[in] filter The filter containing the child filter to use.
1053  * @param[in] for_listing
1054  * @param[in] child_tag The tag of the child filter to use.
1055  */
1056  static bool special_unit_matches(unit_const_ptr & u,
1057  unit_const_ptr & u2,
1058  const map_location & loc,
1059  const_attack_ptr weapon,
1060  const config & filter,
1061  const bool for_listing,
1062  const std::string & child_tag)
1063  {
1064  if (for_listing && !loc.valid())
1065  // The special's context was set to ignore this unit, so assume we pass.
1066  // (This is used by reports.cpp to show active specials when the
1067  // opponent is not known. From a player's perspective, the special
1068  // is active, in that it can be used, even though the player might
1069  // need to select an appropriate opponent.)
1070  return true;
1071 
1072  const config & filter_child = filter.child(child_tag);
1073  if ( !filter_child )
1074  // The special does not filter on this unit, so we pass.
1075  return true;
1076 
1077  // If the primary unit doesn't exist, there's nothing to match
1078  if (!u) {
1079  return false;
1080  }
1081 
1082  unit_filter ufilt{vconfig(filter_child)};
1083 
1084  // If the other unit doesn't exist, try matching without it
1085 
1086 
1087  // Check for a weapon match.
1088  if ( const config & filter_weapon = filter_child.child("filter_weapon") ) {
1089  if ( !weapon || !weapon->matches_filter(filter_weapon) )
1090  return false;
1091  }
1092 
1093  // Passed.
1094  // If the other unit doesn't exist, try matching without it
1095  if (!u2) {
1096  return ufilt.matches(*u, loc);
1097  }
1098  return ufilt.matches(*u, loc, *u2);
1099  }
1100 
1101 }//anonymous namespace
1102 
1103 
1104 //The following functions are intended to allow the use in combat of capacities
1105 //identical to special weapons and therefore to be able to use them on adjacent
1106 //units (abilities of type 'aura') or else on all types of weapons even if the
1107 //beneficiary unit does not have a corresponding weapon
1108 //(defense against ranged weapons abilities for a unit that only has melee attacks)
1109 
1110 unit_ability_list attack_type::get_weapon_ability(const std::string& ability) const
1111 {
1112  const map_location loc = self_ ? self_->get_location() : self_loc_;
1113  unit_ability_list abil_list(loc);
1114  unit_ability_list abil_other_list(loc);
1115  if(self_) {
1116  abil_list.append((*self_).get_abilities(ability, self_loc_));
1117  for(unit_ability_list::iterator i = abil_list.begin(); i != abil_list.end();) {
1118  if(!special_active(*i->ability_cfg, AFFECT_SELF, ability, true, "filter_student")) {
1119  i = abil_list.erase(i);
1120  } else {
1121  ++i;
1122  }
1123  }
1124  }
1125 
1126  if(other_) {
1127  abil_other_list.append((*other_).get_abilities(ability, other_loc_));
1128  for(unit_ability_list::iterator i = abil_other_list.begin(); i != abil_other_list.end();) {
1129  if(!special_active_impl(other_attack_, shared_from_this(), *i->ability_cfg, AFFECT_OTHER, ability, true, "filter_student")) {
1130  i = abil_other_list.erase(i);
1131  } else {
1132  ++i;
1133  }
1134  }
1135  }
1136 
1137  abil_list.append(abil_other_list);
1138  if(!abil_list.empty()){
1139  abil_list = overwrite_special_checking(ability, abil_list, abil_list, "filter_student");
1140  }
1141  return abil_list;
1142 }
1143 
1145 {
1146  unit_ability_list abil_list = get_weapon_ability(special);
1147  unit_ability_list spe_list = get_specials(special);
1148  if(!spe_list.empty()){
1149  spe_list = overwrite_special_checking(special, spe_list, abil_list);
1150  if(special == "plague" && !spe_list.empty()){
1151  return spe_list;
1152  }
1153  abil_list.append(spe_list);
1154  }
1155  return abil_list;
1156 }
1157 
1158 static bool overwrite_special_affects(const config& special)
1159 {
1160  const std::string& apply_to = special["overwrite_specials"];
1161  return (apply_to == "one_side" || apply_to == "both_sides");
1162 }
1163 
1164 unit_ability_list attack_type::overwrite_special_checking(const std::string& ability, unit_ability_list temp_list, const unit_ability_list& abil_list, const std::string& filter_self) const
1165 {
1166  bool overwrite_self = false;
1167  bool overwrite_either = false;
1168 
1169  for(const auto& i : abil_list) {
1170  if((*i.ability_cfg)["overwrite_specials"] == "both_sides") {
1171  overwrite_either = true;
1172  break;
1173  }
1174  if(overwrite_special_affects(*i.ability_cfg) && (special_active(*i.ability_cfg, AFFECT_SELF, ability, true, "filter_student") || special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, true, "filter_student"))) {
1175  overwrite_self = true;
1176  }
1177  }
1178  if(!overwrite_either && !overwrite_self){
1179  return temp_list;
1180  } else if(overwrite_either){
1181  for(unit_ability_list::iterator i = temp_list.begin(); i != temp_list.end();) {
1182  bool overwrite = false;
1183  if(filter_self == "filter_student"){
1184  overwrite = overwrite_special_affects(*i->ability_cfg);
1185  }
1186  if(!overwrite && (special_active(*i->ability_cfg, AFFECT_SELF, ability, true, filter_self) || special_active_impl(other_attack_, shared_from_this(), *i->ability_cfg, AFFECT_OTHER, ability, true, filter_self))) {
1187  i = temp_list.erase(i);
1188  } else {
1189  ++i;
1190  }
1191  }
1192  } else if(overwrite_self){
1193  for(unit_ability_list::iterator i = temp_list.begin(); i != temp_list.end();) {
1194  bool overwrite = false;
1195  if(filter_self == "filter_student"){
1196  overwrite = overwrite_special_affects(*i->ability_cfg);
1197  }
1198  if(!overwrite && special_active(*i->ability_cfg, AFFECT_SELF, ability, true, filter_self)) {
1199  i = temp_list.erase(i);
1200  } else {
1201  ++i;
1202  }
1203  }
1204  }
1205  return temp_list;
1206 }
1207 
1208  /**
1209  * Gets the children of parent (which should be the abilities for an
1210  * attack_type) and places the ones whose tag or id= matches @a id into
1211  * @a tag_result and @a id_result.
1212  * @param tag_result receive the children whose tag matches @a id
1213  * @param id_result receive the children whose id matches @a id
1214  * @param parent the tags whose contain children (abilities here)
1215  * @param id tag or id of child tested
1216  * @param special_id if true, children check by id
1217  * @param special_tags if true, children check by tags
1218  */
1219 static void get_ability_children(std::vector<special_match>& tag_result,
1220  std::vector<special_match>& id_result,
1221  const config& parent, const std::string& id,
1222  bool special_id=true, bool special_tags=true) {
1223  if(special_id && special_tags){
1224  get_special_children(tag_result, id_result, parent, id);
1225  } else if(special_id && !special_tags){
1226  get_special_children_id(id_result, parent, id);
1227  } else if(!special_id && special_tags){
1228  get_special_children_tags(tag_result, parent, id);
1229  }
1230 }
1231 
1232 bool unit::get_self_ability_bool(const config& special, const std::string& tag_name, const map_location& loc) const
1233 {
1234  return (ability_active(tag_name, special, loc) && ability_affects_self(tag_name, special, loc));
1235 }
1236 
1237 bool unit::get_adj_ability_bool(const config& special, const std::string& tag_name, int dir, const map_location& loc, const unit& from) const
1238 {
1239  const auto adjacent = get_adjacent_tiles(loc);
1240  return (affects_side(special, side(), from.side()) && from.ability_active(tag_name, special, adjacent[dir]) && ability_affects_adjacent(tag_name, special, dir, loc, from));
1241 }
1242 
1243 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
1244 {
1245  return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon, false) && ability_affects_weapon(special, opp_weapon, true));
1246 }
1247 
1248 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
1249 {
1250  return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon, false) && ability_affects_weapon(special, opp_weapon, true));
1251 }
1252 
1253 bool attack_type::check_self_abilities(const config& cfg, const std::string& special) const
1254 {
1255  return check_self_abilities_impl(shared_from_this(), other_attack_, cfg, self_, self_loc_, AFFECT_SELF, special, true);
1256 }
1257 
1258 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)
1259 {
1260  if(tag_name == "leadership" && leader_bool){
1261  if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
1262  return true;
1263  }
1264  }
1265  if((*u).checking_tags().count(tag_name) != 0){
1266  if((*u).get_self_ability_bool(special, tag_name, loc) && special_active_impl(self_attack, other_attack, special, whom, tag_name, true, "filter_student")) {
1267  return true;
1268  }
1269  }
1270  return false;
1271 }
1272 
1273 bool attack_type::check_adj_abilities(const config& cfg, const std::string& special, int dir, const unit& from) const
1274 {
1275  return check_adj_abilities_impl(shared_from_this(), other_attack_, cfg, self_, from, dir, self_loc_, AFFECT_SELF, special, true);
1276 }
1277 
1278 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)
1279 {
1280  if(tag_name == "leadership" && leader_bool){
1281  if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
1282  return true;
1283  }
1284  }
1285  if((*u).checking_tags().count(tag_name) != 0){
1286  if((*u).get_adj_ability_bool(special, tag_name, dir, loc, from) && special_active_impl(self_attack, other_attack, special, whom, tag_name, true, "filter_student")) {
1287  return true;
1288  }
1289  }
1290  return false;
1291 }
1292 /**
1293  * Returns whether or not @a *this has a special ability with a tag or id equal to
1294  * @a special. the Check is for a special ability
1295  * active in the current context (see set_specials_context), including
1296  * specials obtained from the opponent's attack.
1297  */
1298 bool attack_type::has_weapon_ability(const std::string& special, bool special_id, bool special_tags) const
1299 {
1300  assert(display::get_singleton());
1301  const unit_map& units = display::get_singleton()->get_units();
1302  if(self_){
1303  std::vector<special_match> special_tag_matches_self;
1304  std::vector<special_match> special_id_matches_self;
1305  get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1306  if(special_tags){
1307  for(const special_match& entry : special_tag_matches_self) {
1308  if(check_self_abilities(*entry.cfg, entry.tag_name)){
1309  return true;
1310  }
1311  }
1312  }
1313  if(special_id){
1314  for(const special_match& entry : special_id_matches_self) {
1315  if(check_self_abilities(*entry.cfg, entry.tag_name)){
1316  return true;
1317  }
1318  }
1319  }
1320 
1321  const auto adjacent = get_adjacent_tiles(self_loc_);
1322  for(unsigned i = 0; i < adjacent.size(); ++i) {
1323  const unit_map::const_iterator it = units.find(adjacent[i]);
1324  if (it == units.end() || it->incapacitated())
1325  continue;
1326  if ( &*it == self_.get() )
1327  continue;
1328 
1329  std::vector<special_match> special_tag_matches_adj;
1330  std::vector<special_match> special_id_matches_adj;
1331  get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1332  if(special_tags){
1333  for(const special_match& entry : special_tag_matches_adj) {
1334  if(check_adj_abilities(*entry.cfg, entry.tag_name, i , *it)){
1335  return true;
1336  }
1337  }
1338  }
1339  if(special_id){
1340  for(const special_match& entry : special_id_matches_adj) {
1341  if(check_adj_abilities(*entry.cfg, entry.tag_name, i , *it)){
1342  return true;
1343  }
1344  }
1345  }
1346  }
1347  }
1348 
1349  if(other_){
1350  std::vector<special_match> special_tag_matches_other;
1351  std::vector<special_match> special_id_matches_other;
1352  get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1353  if(special_tags){
1354  for(const special_match& entry : special_tag_matches_other) {
1355  if(check_self_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, other_loc_, AFFECT_OTHER, entry.tag_name)){
1356  return true;
1357  }
1358  }
1359  }
1360 
1361  if(special_id){
1362  for(const special_match& entry : special_id_matches_other) {
1363  if(check_self_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, other_loc_, AFFECT_OTHER, entry.tag_name)){
1364  return true;
1365  }
1366  }
1367  }
1368 
1369  const auto adjacent = get_adjacent_tiles(other_loc_);
1370  for(unsigned i = 0; i < adjacent.size(); ++i) {
1371  const unit_map::const_iterator it = units.find(adjacent[i]);
1372  if (it == units.end() || it->incapacitated())
1373  continue;
1374  if ( &*it == other_.get() )
1375  continue;
1376 
1377  std::vector<special_match> special_tag_matches_oadj;
1378  std::vector<special_match> special_id_matches_oadj;
1379  get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1380  if(special_tags){
1381  for(const special_match& entry : special_tag_matches_oadj) {
1382  if(check_adj_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *it, i, other_loc_, AFFECT_OTHER, entry.tag_name)){
1383  return true;
1384  }
1385  }
1386  }
1387 
1388  if(special_id){
1389  for(const special_match& entry : special_id_matches_oadj) {
1390  if(check_adj_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *it, i, other_loc_, AFFECT_OTHER, entry.tag_name)){
1391  return true;
1392  }
1393  }
1394  }
1395  }
1396  }
1397  return false;
1398 }
1399 
1400 bool attack_type::has_special_or_ability(const std::string& special, bool special_id, bool special_tags) const
1401 {
1402  return (has_special(special, false, special_id, special_tags) || has_weapon_ability(special, special_id, special_tags));
1403 }
1404 //end of emulate weapon special functions.
1405 
1406 bool attack_type::special_active(const config& special, AFFECTS whom, const std::string& tag_name,
1407  bool include_backstab, const std::string& filter_self) const
1408 {
1409  return special_active_impl(shared_from_this(), other_attack_, special, whom, tag_name, include_backstab, filter_self);
1410 }
1411 
1412 /**
1413  * Returns whether or not the given special is active for the specified unit,
1414  * based on the current context (see set_specials_context).
1415  * @param self_attack this unit's attack
1416  * @param other_attack the other unit's attack
1417  * @param special a weapon special WML structure
1418  * @param whom specifies which combatant we care about
1419  * @param tag_name tag name of the special config
1420  * @param include_backstab false if backstab specials should not be active
1421  * (usually true since backstab is usually accounted
1422  * for elsewhere)
1423  * @param filter_self the filter to use
1424  */
1425 bool attack_type::special_active_impl(const_attack_ptr self_attack, const_attack_ptr other_attack, const config& special, AFFECTS whom, const std::string& tag_name,
1426  bool include_backstab, const std::string& filter_self)
1427 {
1428  assert(self_attack || other_attack);
1429  bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
1430  bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
1431  //log_scope("special_active");
1432 
1433  // Backstab check
1434  if ( !include_backstab )
1435  if ( special["backstab"].to_bool() )
1436  return false;
1437 
1438  // Does this affect the specified unit?
1439  if ( whom == AFFECT_SELF ) {
1440  if ( !special_affects_self(special, is_attacker) )
1441  return false;
1442  }
1443  if ( whom == AFFECT_OTHER ) {
1444  if ( !special_affects_opponent(special, is_attacker) )
1445  return false;
1446  }
1447 
1448  // Is this active on attack/defense?
1449  const std::string & active_on = special["active_on"];
1450  if ( !active_on.empty() ) {
1451  if ( is_attacker && active_on != "offense" )
1452  return false;
1453  if ( !is_attacker && active_on != "defense" )
1454  return false;
1455  }
1456 
1457  // Get the units involved.
1458  assert(display::get_singleton());
1459  const unit_map& units = display::get_singleton()->get_units();
1460 
1461  unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
1462  unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
1463  map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
1464  map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
1465  //TODO: why is this needed?
1466  if(self == nullptr) {
1467  unit_map::const_iterator it = units.find(self_loc);
1468  if(it.valid()) {
1469  self = it.get_shared_ptr();
1470  }
1471  }
1472  if(other == nullptr) {
1473  unit_map::const_iterator it = units.find(other_loc);
1474  if(it.valid()) {
1475  other = it.get_shared_ptr();
1476  }
1477  }
1478 
1479  // Make sure they're facing each other.
1480  temporary_facing self_facing(self, self_loc.get_relative_dir(other_loc));
1481  temporary_facing other_facing(other, other_loc.get_relative_dir(self_loc));
1482 
1483  // Filter poison, plague, drain, first strike
1484  if (tag_name == "drains" && other && other->get_state("undrainable")) {
1485  return false;
1486  }
1487  if (tag_name == "plague" && other &&
1488  (other->get_state("unplagueable") ||
1489  resources::gameboard->map().is_village(other_loc))) {
1490  return false;
1491  }
1492  if (tag_name == "poison" && other &&
1493  (other->get_state("unpoisonable") || other->get_state(unit::STATE_POISONED))) {
1494  return false;
1495  }
1496  if (tag_name == "firststrike" && !is_attacker && other_attack &&
1497  other_attack->has_special_or_ability("firststrike")) {
1498  return false;
1499  }
1500 
1501 
1502  // Translate our context into terms of "attacker" and "defender".
1503  unit_const_ptr & att = is_attacker ? self : other;
1504  unit_const_ptr & def = is_attacker ? other : self;
1505  const map_location & att_loc = is_attacker ? self_loc : other_loc;
1506  const map_location & def_loc = is_attacker ? other_loc : self_loc;
1507  const_attack_ptr att_weapon = is_attacker ? self_attack : other_attack;
1508  const_attack_ptr def_weapon = is_attacker ? other_attack : self_attack;
1509 
1510  // Filter the units involved.
1511  if (!special_unit_matches(self, other, self_loc, self_attack, special, is_for_listing, filter_self))
1512  return false;
1513  if (!special_unit_matches(other, self, other_loc, other_attack, special, is_for_listing, "filter_opponent"))
1514  return false;
1515  if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing, "filter_attacker"))
1516  return false;
1517  if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing, "filter_defender"))
1518  return false;
1519 
1520  const auto adjacent = get_adjacent_tiles(self_loc);
1521 
1522  // Filter the adjacent units.
1523  for (const config &i : special.child_range("filter_adjacent"))
1524  {
1525  std::size_t count = 0;
1526  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
1527  unit_filter filter{ vconfig(i) };
1528  for (const map_location::DIRECTION index : dirs)
1529  {
1531  continue;
1532  unit_map::const_iterator unit = units.find(adjacent[index]);
1533  if (unit == units.end() || !filter.matches(*unit, adjacent[index], *self))
1534  return false;
1535  if (i.has_attribute("is_enemy")) {
1537  if (i["is_enemy"].to_bool() != dc.get_team(unit->side()).is_enemy(self->side())) {
1538  continue;
1539  }
1540  }
1541  count++;
1542  }
1543  if (i["count"].empty() && count != dirs.size()) {
1544  return false;
1545  }
1546  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
1547  return false;
1548  }
1549  }
1550 
1551  // Filter the adjacent locations.
1552  for (const config &i : special.child_range("filter_adjacent_location"))
1553  {
1554  std::size_t count = 0;
1555  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
1556  terrain_filter adj_filter(vconfig(i), resources::filter_con, false);
1557  for (const map_location::DIRECTION index : dirs)
1558  {
1560  continue;
1561  if(!adj_filter.match(adjacent[index])) {
1562  return false;
1563  }
1564  count++;
1565  }
1566  if (i["count"].empty() && count != dirs.size()) {
1567  return false;
1568  }
1569  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
1570  return false;
1571  }
1572  }
1573 
1574  return true;
1575 }
1576 
1577 
1578 
1580 {
1581 
1582 void individual_effect::set(value_modifier t, int val, const config *abil, const map_location &l)
1583 {
1584  type=t;
1585  value=val;
1586  ability=abil;
1587  loc=l;
1588 }
1589 
1590 bool filter_base_matches(const config& cfg, int def)
1591 {
1592  if (const config &apply_filter = cfg.child("filter_base_value")) {
1593  config::attribute_value cond_eq = apply_filter["equals"];
1594  config::attribute_value cond_ne = apply_filter["not_equals"];
1595  config::attribute_value cond_lt = apply_filter["less_than"];
1596  config::attribute_value cond_gt = apply_filter["greater_than"];
1597  config::attribute_value cond_ge = apply_filter["greater_than_equal_to"];
1598  config::attribute_value cond_le = apply_filter["less_than_equal_to"];
1599  return (cond_eq.empty() || def == cond_eq.to_int()) &&
1600  (cond_ne.empty() || def != cond_ne.to_int()) &&
1601  (cond_lt.empty() || def < cond_lt.to_int()) &&
1602  (cond_gt.empty() || def > cond_gt.to_int()) &&
1603  (cond_ge.empty() || def >= cond_ge.to_int()) &&
1604  (cond_le.empty() || def <= cond_le.to_int());
1605  }
1606  return true;
1607 }
1608 
1609 effect::effect(const unit_ability_list& list, int def, bool backstab, const_attack_ptr att) :
1610  effect_list_(),
1611  composite_value_(0)
1612 {
1613 
1614  int value_set = def;
1615  std::map<std::string,individual_effect> values_add;
1616  std::map<std::string,individual_effect> values_mul;
1617  std::map<std::string,individual_effect> values_div;
1618 
1619  individual_effect set_effect_max;
1620  individual_effect set_effect_min;
1621 
1622  for (const unit_ability & ability : list) {
1623  const config& cfg = *ability.ability_cfg;
1624  const std::string& effect_id = cfg[cfg["id"].empty() ? "name" : "id"];
1625 
1626  if (!cfg["backstab"].blank()) {
1627  deprecated_message("backstab= in weapon specials", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use [filter_adjacent] instead.");
1628  }
1629 
1630  if (!backstab && cfg["backstab"].to_bool())
1631  continue;
1632  if (!filter_base_matches(cfg, def))
1633  continue;
1634 
1635  if (const config::attribute_value *v = cfg.get("value")) {
1636  int value = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1637  callable.add("base_value", wfl::variant(def));
1638  return formula.evaluate(callable).as_int();
1639  });
1640 
1641  int value_cum = cfg["cumulative"].to_bool() ? std::max(def, value) : value;
1642  assert((set_effect_min.type != NOT_USED) == (set_effect_max.type != NOT_USED));
1643  if(set_effect_min.type == NOT_USED) {
1644  set_effect_min.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1645  set_effect_max.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1646  }
1647  else {
1648  if(value_cum > set_effect_max.value) {
1649  set_effect_max.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1650  }
1651  if(value_cum < set_effect_min.value) {
1652  set_effect_min.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1653  }
1654  }
1655  }
1656 
1657  if (const config::attribute_value *v = cfg.get("add")) {
1658  int add = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1659  callable.add("base_value", wfl::variant(def));
1660  return formula.evaluate(callable).as_int();
1661  });
1662  std::map<std::string,individual_effect>::iterator add_effect = values_add.find(effect_id);
1663  if(add_effect == values_add.end() || add > add_effect->second.value) {
1664  values_add[effect_id].set(ADD, add, ability.ability_cfg, ability.teacher_loc);
1665  }
1666  }
1667  if (const config::attribute_value *v = cfg.get("sub")) {
1668  int sub = - get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1669  callable.add("base_value", wfl::variant(def));
1670  return formula.evaluate(callable).as_int();
1671  });
1672  std::map<std::string,individual_effect>::iterator sub_effect = values_add.find(effect_id);
1673  if(sub_effect == values_add.end() || sub < sub_effect->second.value) {
1674  values_add[effect_id].set(ADD, sub, ability.ability_cfg, ability.teacher_loc);
1675  }
1676  }
1677  if (const config::attribute_value *v = cfg.get("multiply")) {
1678  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) {
1679  callable.add("base_value", wfl::variant(def));
1680  return formula.evaluate(callable).as_decimal() / 1000.0 ;
1681  }) * 100);
1682  std::map<std::string,individual_effect>::iterator mul_effect = values_mul.find(effect_id);
1683  if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
1684  values_mul[effect_id].set(MUL, multiply, ability.ability_cfg, ability.teacher_loc);
1685  }
1686  }
1687  if (const config::attribute_value *v = cfg.get("divide")) {
1688  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) {
1689  callable.add("base_value", wfl::variant(def));
1690  return formula.evaluate(callable).as_decimal() / 1000.0 ;
1691  }) * 100);
1692 
1693  if (divide == 0) {
1694  ERR_NG << "division by zero with divide= in ability/weapon special " << effect_id << std::endl;
1695  }
1696  else {
1697  std::map<std::string,individual_effect>::iterator div_effect = values_div.find(effect_id);
1698  if(div_effect == values_div.end() || divide > div_effect->second.value) {
1699  values_div[effect_id].set(DIV, divide, ability.ability_cfg, ability.teacher_loc);
1700  }
1701  }
1702  }
1703  }
1704 
1705  if(set_effect_max.type != NOT_USED) {
1706  value_set = std::max(set_effect_max.value, 0) + std::min(set_effect_min.value, 0);
1707  if(set_effect_max.value > def) {
1708  effect_list_.push_back(set_effect_max);
1709  }
1710  if(set_effect_min.value < def) {
1711  effect_list_.push_back(set_effect_min);
1712  }
1713  }
1714 
1715  /* Do multiplication with floating point values rather than integers
1716  * We want two places of precision for each multiplier
1717  * Using integers multiplied by 100 to keep precision causes overflow
1718  * after 3-4 abilities for 32-bit values and ~8 for 64-bit
1719  * Avoiding the overflow by dividing after each step introduces rounding errors
1720  * that may vary depending on the order effects are applied
1721  * As the final values are likely <1000 (always true for mainline), loss of less significant digits is not an issue
1722  */
1723  double multiplier = 1.0;
1724  double divisor = 1.0;
1725 
1726  for(const auto& val : values_mul) {
1727  multiplier *= val.second.value/100.0;
1728  effect_list_.push_back(val.second);
1729  }
1730 
1731  for(const auto& val : values_div) {
1732  divisor *= val.second.value/100.0;
1733  effect_list_.push_back(val.second);
1734  }
1735 
1736  int addition = 0;
1737  for(const auto& val : values_add) {
1738  addition += val.second.value;
1739  effect_list_.push_back(val.second);
1740  }
1741 
1742  composite_value_ = static_cast<int>((value_set + addition) * multiplier / divisor);
1743 }
1744 
1745 } // end namespace unit_abilities
bool empty() const
Tests for an attribute that either was never set or was set to "".
std::string weapon_specials(bool only_active=false, bool is_backstab=false) const
Returns a comma-separated string of active names for the specials of *this.
Definition: abilities.cpp:837
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:453
std::vector< individual_effect > effect_list_
Definition: abilities.hpp:53
bool empty() const
Definition: unit.hpp:92
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:414
unit_iterator end()
Definition: map.hpp:428
#define ERR_NG
Definition: abilities.cpp:46
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:91
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:953
const team & get_team(int side) const
virtual const display_context & get_disp_context() const =0
bool special_active(const config &special, AFFECTS whom, const std::string &tag_name, bool include_backstab=true, const std::string &filter_self="filter_self") const
Definition: abilities.cpp:1406
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
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:175
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:226
This class represents a single unit of a specific type.
Definition: unit.hpp:120
const color_t inactive_details_color
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:39
unit_filter & set_use_flat_tod(bool value)
Definition: filter.hpp:121
void emplace_back(T &&... args)
Definition: unit.hpp:101
iterator erase(const iterator &erase_it)
Definition: unit.hpp:98
Variant for storing WML attributes.
std::string filename
Definition: formula.hpp:107
New lexcical_cast header.
int as_int() const
Definition: variant.cpp:294
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:1232
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:406
void set(value_modifier t, int val, const config *abil, const map_location &l)
Definition: abilities.cpp:1582
child_itors child_range(config_key_type key)
Definition: config.cpp:356
map_location student_loc
Used by the formula in the ability.
Definition: unit.hpp:54
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:1278
map_location other_loc_
virtual const gamemap & map() const override
Definition: game_board.hpp:101
void modified_attacks(bool is_backstab, unsigned &min_attacks, unsigned &max_attacks) const
Calculates the number of attacks this weapon has, considering specials.
Definition: abilities.cpp:963
The unit is slowed - it moves slower and does less damage.
Definition: unit.hpp:854
const unit_map & get_units() const
Definition: display.hpp:124
bool has_ability_type(const std::string &ability) const
Check if the unit has an ability of a specific type.
Definition: abilities.cpp:466
unit_const_ptr other_
const std::string & type() const
Definition: attack_type.hpp:44
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
Definition: filter.hpp:131
#define d
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
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:1237
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:25
int num_attacks() const
Definition: attack_type.hpp:53
std::vector< std::tuple< std::string, t_string, t_string, t_string > > ability_tooltips() const
Gets the names and descriptions of this unit&#39;s abilities.
Definition: abilities.cpp:322
A single unit type that the player may recruit.
Definition: types.hpp:44
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:1590
std::vector< unit_ability >::iterator iterator
Definition: unit.hpp:83
map_location loc_
unit_ptr find_unit_ptr(const T &val)
Definition: map.hpp:387
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:26
std::string span_color(const color_t &color)
Returns a Pango formatting string using the provided color_t object.
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
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:1253
Data typedef for unit_ability_list.
Definition: unit.hpp:39
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:59
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
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_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:252
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:421
filter_context * filter_con
Definition: resources.cpp:23
bool valid() const
Definition: location.hpp:88
specials_context_t(const attack_type &weapon, bool attacking)
Initialize weapon specials context for listing.
Definition: abilities.cpp:931
std::string type
Definition: formula.hpp:105
bool blank() const
Tests for an attribute that was never set.
game_board * gameboard
Definition: resources.cpp:20
bool is_enemy(int n) const
Definition: team.hpp:251
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:1400
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:801
map_display and display: classes which take care of displaying the map and game-data on the screen...
std::pair< int, map_location > get_extremum(const std::string &key, int def, const TComp &comp) const
Definition: abilities.cpp:547
unit_ability_list overwrite_special_checking(const std::string &ability, unit_ability_list temp_list, const unit_ability_list &abil_list, const std::string &filter_self="filter_self") const
overwrite_special_checking : return an unit_ability_list list after checking presence or not of overw...
Definition: abilities.cpp:1164
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:768
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:1243
Encapsulates the map of the game.
Definition: location.hpp:37
unit_iterator find(std::size_t id)
Definition: map.cpp:309
#define UNUSED(x)
Definition: global.hpp:30
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:145
pointer get_shared_ptr() const
This is exactly the same as operator-> but it&#39;s slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:217
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:1298
std::size_t i
Definition: function.cpp:940
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:288
bool is_for_listing_
int damage() const
Definition: attack_type.hpp:52
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:1248
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
Definition: abilities.cpp:1110
mock_party p
static map_location::DIRECTION s
iterator begin()
Definition: unit.hpp:86
unit_const_ptr self_
int modified_damage(bool is_backstab) const
Returns the damage per attack of this weapon, considering specials.
Definition: abilities.cpp:990
const display_context & get_disp_context() const
Definition: display.hpp:171
int get_composite_value() const
Definition: abilities.hpp:46
void append(const unit_ability_list &other)
Appens the abilities from other to this, ignores other.loc()
Definition: unit.hpp:106
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:39
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:71
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:1219
void add_formula_context(wfl::map_formula_callable &) const
Definition: abilities.cpp:471
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:445
config & cfg
Definition: config.hpp:569
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, bool include_backstab=true, 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:1425
bool is_village(const map_location &loc) const
Definition: map.cpp:65
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:1273
static bool overwrite_special_affects(const config &special)
Definition: abilities.cpp:1158
bool empty() const
Definition: tstring.hpp:186
double t
Definition: astarsearch.cpp:64
unit_ability_list get_specials_and_abilities(const std::string &special) const
Returns list who contains get_weapon_ability and get_specials list for each ability type...
Definition: abilities.cpp:1144
const_attack_ptr other_attack_
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Standard logging facilities (interface).
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
static const map_location & null_location()
Definition: location.hpp:80
Container associating units to locations.
Definition: map.hpp:97
#define e
int side() const
The side this unit belongs to.
Definition: unit.hpp:333
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
Definition: abilities.cpp:238
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Gets the unit&#39;s active abilities of a particular type if it were on a specified location.
Definition: abilities.cpp:184
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:225
std::shared_ptr< const attack_type > parent
bool ability_active(const std::string &ability, const config &cfg, const map_location &loc) const
Check if an ability is active.
Definition: abilities.cpp:350
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
bool valid() const
Definition: map.hpp:273
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:1258
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:33
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
bool empty() const
Definition: config.cpp:916
map_location self_loc_
static lg::log_domain log_engine("engine")
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:696
iterator end()
Definition: unit.hpp:88
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: unit.hpp:70