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