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  * static used in weapon_specials (bool only_active, bool is_backstab) and
836  * @return a string and a set_string for the weapon_specials function below.
837  * @param[in,out] weapon_abilities the string modified and returned
838  * @param[in] active the boolean for determine if @name can be added or not
839  * @param[in] sp reference to ability to check
840  * @param[in,out] checking_name the reference for checking if @name already added
841  */
842 static void add_name(std::string& weapon_abilities, bool active, const config::any_child sp, std::set<std::string>& checking_name, bool affect_adjacent)
843 {
844  if (active) {
845  const std::string& name = sp.cfg["name"].str();
846 
847  if (!name.empty() && checking_name.count(name) == 0) {
848  checking_name.insert(name);
849  if (!weapon_abilities.empty()) weapon_abilities += ", ";
850  if (affect_adjacent) {
851  weapon_abilities += sp.cfg["affect_enemies"].to_bool() ? font::span_color(font::BAD_COLOR, name) : font::span_color(font::GOOD_COLOR, name);
852  } else {
853  weapon_abilities += font::span_color(font::BUTTON_COLOR, name);
854  }
855  }
856  }
857 }
858 
859 /**
860  * Returns a comma-separated string of active names for the specials of *this.
861  * Empty names are skipped.
862  *
863  * This excludes inactive specials if only_active is true. Whether or not a
864  * special is active depends on the current context (see set_specials_context)
865  * and the @a is_backstab parameter.
866  */
867 std::string attack_type::weapon_specials(bool only_active, bool is_backstab) const
868 {
869  //log_scope("weapon_specials");
870  std::string res;
871  for (const config::any_child sp : specials_.all_children_range())
872  {
873  const bool active = special_active(sp.cfg, AFFECT_EITHER, sp.key, is_backstab);
874 
875  const std::string& name =
876  active
877  ? sp.cfg["name"].str()
878  : default_value(sp.cfg, "name_inactive", "name").str();
879  if (!name.empty()) {
880  if (!res.empty()) res += ", ";
881  if (only_active && !active) res += font::span_color(font::inactive_details_color);
882  res += name;
883  if (only_active && !active) res += "</span>";
884  }
885  }
886  std::string weapon_abilities;
887  std::set<std::string> checking_name;
888  assert(display::get_singleton());
889  const unit_map& units = display::get_singleton()->get_units();
890  if(self_){
891  for (const config::any_child sp : self_->abilities().all_children_range()){
892  const bool active = check_self_abilities_impl(shared_from_this(), other_attack_, sp.cfg, self_, self_loc_, AFFECT_SELF, sp.key);
893  add_name(weapon_abilities, active, sp, checking_name, false);
894  }
895  const auto adjacent = get_adjacent_tiles(self_loc_);
896  for(unsigned i = 0; i < adjacent.size(); ++i) {
897  const unit_map::const_iterator it = units.find(adjacent[i]);
898  if (it == units.end() || it->incapacitated())
899  continue;
900  if(&*it == self_.get())
901  continue;
902  for (const config::any_child sp : it->abilities().all_children_range()){
903  const bool active = check_adj_abilities_impl(shared_from_this(), other_attack_, sp.cfg, self_, *it, i, self_loc_, AFFECT_SELF, sp.key);
904  add_name(weapon_abilities, active, sp, checking_name, true);
905  }
906  }
907  }
908  if(!weapon_abilities.empty() && !res.empty()) {
909  weapon_abilities = ", \n" + weapon_abilities;
910  res += weapon_abilities;
911  } else if (!weapon_abilities.empty()){
912  res = weapon_abilities;
913  }
914  return res;
915 }
916 
917 
918 /**
919  * Sets the context under which specials will be checked for being active.
920  * This version is appropriate if both units in a combat are known.
921  * @param[in] weapon The weapon being considered.
922  * @param[in] self A reference to the unit with this weapon.
923  * @param[in] other A reference to the other unit in the combat.
924  * @param[in] unit_loc The location of the unit with this weapon.
925  * @param[in] other_loc The location of the other unit in the combat.
926  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
927  * @param[in] other_attack The attack used by the other unit.
928  */
930  const_attack_ptr other_attack,
931  unit_const_ptr self,
932  unit_const_ptr other,
933  const map_location& unit_loc,
934  const map_location& other_loc,
935  bool attacking)
936  : parent(weapon.shared_from_this())
937 {
938  weapon.self_ = self;
939  weapon.other_ = other;
940  weapon.self_loc_ = unit_loc;
941  weapon.other_loc_ = other_loc;
942  weapon.is_attacker_ = attacking;
943  weapon.other_attack_ = other_attack;
944  weapon.is_for_listing_ = false;
945 }
946 
947 /**
948  * Sets the context under which specials will be checked for being active.
949  * This version is appropriate if there is no specific combat being considered.
950  * @param[in] weapon The weapon being considered.
951  * @param[in] self A reference to the unit with this weapon.
952  * @param[in] loc The location of the unit with this weapon.
953  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
954  */
956  : parent(weapon.shared_from_this())
957 {
958  weapon.self_ = self;
959  weapon.other_ = unit_ptr();
960  weapon.self_loc_ = loc;
962  weapon.is_attacker_ = attacking;
963  weapon.other_attack_ = nullptr;
964  weapon.is_for_listing_ = false;
965 }
966 
967 /**
968  * Sets the context under which specials will be checked for being active.
969  * This version is appropriate for theoretical units of a particular type.
970  * @param[in] weapon The weapon being considered.
971  * @param[in] self_type A reference to the type of the unit with this weapon.
972  * @param[in] loc The location of the unit with this weapon.
973  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
974  */
975 attack_type::specials_context_t::specials_context_t(const attack_type& weapon, const unit_type& self_type, const map_location& loc, bool attacking)
976  : parent(weapon.shared_from_this())
977 {
978  UNUSED(self_type);
979  weapon.self_ = unit_ptr();
980  weapon.other_ = unit_ptr();
981  weapon.self_loc_ = loc;
983  weapon.is_attacker_ = attacking;
984  weapon.other_attack_ = nullptr;
985  weapon.is_for_listing_ = false;
986 }
987 
989  : parent(weapon.shared_from_this())
990 {
991  weapon.is_for_listing_ = true;
992  weapon.is_attacker_ = attacking;
993 }
994 
996 {
997  if(was_moved) return;
998  parent->self_ = unit_ptr();
999  parent->other_ = unit_ptr();
1000  parent->self_loc_ = map_location::null_location();
1001  parent->other_loc_ = map_location::null_location();
1002  parent->is_attacker_ = false;
1003  parent->other_attack_ = nullptr;
1004  parent->is_for_listing_ = false;
1005 }
1006 
1008  : parent(other.parent)
1009 {
1010  other.was_moved = true;
1011 }
1012 
1013 /**
1014  * Calculates the number of attacks this weapon has, considering specials.
1015  * This returns two numbers because of the swarm special. The actual number of
1016  * attacks depends on the unit's health and should be:
1017  * min_attacks + (max_attacks - min_attacks) * (current hp) / (max hp)
1018  * c.f. swarm_blows()
1019  */
1020 void attack_type::modified_attacks(bool is_backstab, unsigned & min_attacks,
1021  unsigned & max_attacks) const
1022 {
1023  // Apply [attacks].
1024  unit_abilities::effect attacks_effect(get_specials_and_abilities("attacks"),
1025  num_attacks(), is_backstab, shared_from_this());
1026  int attacks_value = attacks_effect.get_composite_value();
1027 
1028  if ( attacks_value < 0 ) {
1029  attacks_value = num_attacks();
1030  ERR_NG << "negative number of strikes after applying weapon specials" << std::endl;
1031  }
1032 
1033  // Apply [swarm].
1034  unit_ability_list swarm_specials = get_specials_and_abilities("swarm");
1035  if ( !swarm_specials.empty() ) {
1036  min_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_min").first);
1037  max_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_max", attacks_value).first);
1038  } else {
1039  min_attacks = max_attacks = attacks_value;
1040  }
1041 }
1042 
1043 
1044 /**
1045  * Returns the damage per attack of this weapon, considering specials.
1046  */
1047 int attack_type::modified_damage(bool is_backstab) const
1048 {
1049  unit_abilities::effect dmg_effect(get_specials_and_abilities("damage"), damage(), is_backstab, shared_from_this());
1050  int damage_value = dmg_effect.get_composite_value();
1051  return damage_value;
1052 }
1053 
1054 
1055 namespace { // Helpers for attack_type::special_active()
1056 
1057  /**
1058  * Returns whether or not the given special affects the opponent of the unit
1059  * with the special.
1060  * @param[in] special a weapon special WML structure
1061  * @param[in] is_attacker whether or not the unit with the special is the attacker
1062  */
1063  bool special_affects_opponent(const config& special, bool is_attacker)
1064  {
1065  //log_scope("special_affects_opponent");
1066  const std::string& apply_to = special["apply_to"];
1067  if ( apply_to.empty() )
1068  return false;
1069  if ( apply_to == "both" )
1070  return true;
1071  if ( apply_to == "opponent" )
1072  return true;
1073  if ( is_attacker && apply_to == "defender" )
1074  return true;
1075  if ( !is_attacker && apply_to == "attacker" )
1076  return true;
1077  return false;
1078  }
1079 
1080  /**
1081  * Returns whether or not the given special affects the unit with the special.
1082  * @param[in] special a weapon special WML structure
1083  * @param[in] is_attacker whether or not the unit with the special is the attacker
1084  */
1085  bool special_affects_self(const config& special, bool is_attacker)
1086  {
1087  //log_scope("special_affects_self");
1088  const std::string& apply_to = special["apply_to"];
1089  if ( apply_to.empty() )
1090  return true;
1091  if ( apply_to == "both" )
1092  return true;
1093  if ( apply_to == "self" )
1094  return true;
1095  if ( is_attacker && apply_to == "attacker" )
1096  return true;
1097  if ( !is_attacker && apply_to == "defender")
1098  return true;
1099  return false;
1100  }
1101 
1102  /**
1103  * Determines if a unit/weapon combination matches the specified child
1104  * (normally a [filter_*] child) of the provided filter.
1105  * @param[in] u A unit to filter.
1106  * @param[in] u2 Another unit to filter.
1107  * @param[in] loc The presumed location of @a un_it.
1108  * @param[in] weapon The attack_type to filter.
1109  * @param[in] filter The filter containing the child filter to use.
1110  * @param[in] for_listing
1111  * @param[in] child_tag The tag of the child filter to use.
1112  */
1113  static bool special_unit_matches(unit_const_ptr & u,
1114  unit_const_ptr & u2,
1115  const map_location & loc,
1116  const_attack_ptr weapon,
1117  const config & filter,
1118  const bool for_listing,
1119  const std::string & child_tag)
1120  {
1121  if (for_listing && !loc.valid())
1122  // The special's context was set to ignore this unit, so assume we pass.
1123  // (This is used by reports.cpp to show active specials when the
1124  // opponent is not known. From a player's perspective, the special
1125  // is active, in that it can be used, even though the player might
1126  // need to select an appropriate opponent.)
1127  return true;
1128 
1129  const config & filter_child = filter.child(child_tag);
1130  if ( !filter_child )
1131  // The special does not filter on this unit, so we pass.
1132  return true;
1133 
1134  // If the primary unit doesn't exist, there's nothing to match
1135  if (!u) {
1136  return false;
1137  }
1138 
1139  unit_filter ufilt{vconfig(filter_child)};
1140 
1141  // If the other unit doesn't exist, try matching without it
1142 
1143 
1144  // Check for a weapon match.
1145  if ( const config & filter_weapon = filter_child.child("filter_weapon") ) {
1146  if ( !weapon || !weapon->matches_filter(filter_weapon) )
1147  return false;
1148  }
1149 
1150  // Passed.
1151  // If the other unit doesn't exist, try matching without it
1152  if (!u2) {
1153  return ufilt.matches(*u, loc);
1154  }
1155  return ufilt.matches(*u, loc, *u2);
1156  }
1157 
1158 }//anonymous namespace
1159 
1160 
1161 //The following functions are intended to allow the use in combat of capacities
1162 //identical to special weapons and therefore to be able to use them on adjacent
1163 //units (abilities of type 'aura') or else on all types of weapons even if the
1164 //beneficiary unit does not have a corresponding weapon
1165 //(defense against ranged weapons abilities for a unit that only has melee attacks)
1166 
1167 unit_ability_list attack_type::get_weapon_ability(const std::string& ability) const
1168 {
1169  const map_location loc = self_ ? self_->get_location() : self_loc_;
1170  unit_ability_list abil_list(loc);
1171  unit_ability_list abil_other_list(loc);
1172  if(self_) {
1173  abil_list.append((*self_).get_abilities(ability, self_loc_));
1174  for(unit_ability_list::iterator i = abil_list.begin(); i != abil_list.end();) {
1175  if(!special_active(*i->ability_cfg, AFFECT_SELF, ability, true, "filter_student")) {
1176  i = abil_list.erase(i);
1177  } else {
1178  ++i;
1179  }
1180  }
1181  }
1182 
1183  if(other_) {
1184  abil_other_list.append((*other_).get_abilities(ability, other_loc_));
1185  for(unit_ability_list::iterator i = abil_other_list.begin(); i != abil_other_list.end();) {
1186  if(!special_active_impl(other_attack_, shared_from_this(), *i->ability_cfg, AFFECT_OTHER, ability, true, "filter_student")) {
1187  i = abil_other_list.erase(i);
1188  } else {
1189  ++i;
1190  }
1191  }
1192  }
1193 
1194  abil_list.append(abil_other_list);
1195  if(!abil_list.empty()){
1196  abil_list = overwrite_special_checking(ability, abil_list, abil_list, "filter_student");
1197  }
1198  return abil_list;
1199 }
1200 
1202 {
1203  unit_ability_list abil_list = get_weapon_ability(special);
1204  unit_ability_list spe_list = get_specials(special);
1205  if(!spe_list.empty()){
1206  spe_list = overwrite_special_checking(special, spe_list, abil_list);
1207  if(special == "plague" && !spe_list.empty()){
1208  return spe_list;
1209  }
1210  abil_list.append(spe_list);
1211  }
1212  return abil_list;
1213 }
1214 
1215 static bool overwrite_special_affects(const config& special)
1216 {
1217  const std::string& apply_to = special["overwrite_specials"];
1218  return (apply_to == "one_side" || apply_to == "both_sides");
1219 }
1220 
1221 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
1222 {
1223  bool overwrite_self = false;
1224  bool overwrite_either = false;
1225 
1226  for(const auto& i : abil_list) {
1227  if((*i.ability_cfg)["overwrite_specials"] == "both_sides") {
1228  overwrite_either = true;
1229  break;
1230  }
1231  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"))) {
1232  overwrite_self = true;
1233  }
1234  }
1235  if(!overwrite_either && !overwrite_self){
1236  return temp_list;
1237  } else if(overwrite_either){
1238  for(unit_ability_list::iterator i = temp_list.begin(); i != temp_list.end();) {
1239  bool overwrite = false;
1240  if(filter_self == "filter_student"){
1241  overwrite = overwrite_special_affects(*i->ability_cfg);
1242  }
1243  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))) {
1244  i = temp_list.erase(i);
1245  } else {
1246  ++i;
1247  }
1248  }
1249  } else if(overwrite_self){
1250  for(unit_ability_list::iterator i = temp_list.begin(); i != temp_list.end();) {
1251  bool overwrite = false;
1252  if(filter_self == "filter_student"){
1253  overwrite = overwrite_special_affects(*i->ability_cfg);
1254  }
1255  if(!overwrite && special_active(*i->ability_cfg, AFFECT_SELF, ability, true, filter_self)) {
1256  i = temp_list.erase(i);
1257  } else {
1258  ++i;
1259  }
1260  }
1261  }
1262  return temp_list;
1263 }
1264 
1265  /**
1266  * Gets the children of parent (which should be the abilities for an
1267  * attack_type) and places the ones whose tag or id= matches @a id into
1268  * @a tag_result and @a id_result.
1269  * @param tag_result receive the children whose tag matches @a id
1270  * @param id_result receive the children whose id matches @a id
1271  * @param parent the tags whose contain children (abilities here)
1272  * @param id tag or id of child tested
1273  * @param special_id if true, children check by id
1274  * @param special_tags if true, children check by tags
1275  */
1276 static void get_ability_children(std::vector<special_match>& tag_result,
1277  std::vector<special_match>& id_result,
1278  const config& parent, const std::string& id,
1279  bool special_id=true, bool special_tags=true) {
1280  if(special_id && special_tags){
1281  get_special_children(tag_result, id_result, parent, id);
1282  } else if(special_id && !special_tags){
1283  get_special_children_id(id_result, parent, id);
1284  } else if(!special_id && special_tags){
1285  get_special_children_tags(tag_result, parent, id);
1286  }
1287 }
1288 
1289 bool unit::get_self_ability_bool(const config& special, const std::string& tag_name, const map_location& loc) const
1290 {
1291  return (ability_active(tag_name, special, loc) && ability_affects_self(tag_name, special, loc));
1292 }
1293 
1294 bool unit::get_adj_ability_bool(const config& special, const std::string& tag_name, int dir, const map_location& loc, const unit& from) const
1295 {
1296  const auto adjacent = get_adjacent_tiles(loc);
1297  return (affects_side(special, side(), from.side()) && from.ability_active(tag_name, special, adjacent[dir]) && ability_affects_adjacent(tag_name, special, dir, loc, from));
1298 }
1299 
1300 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
1301 {
1302  return (get_self_ability_bool(special, tag_name, loc) && ability_affects_weapon(special, weapon, false) && ability_affects_weapon(special, opp_weapon, true));
1303 }
1304 
1305 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
1306 {
1307  return (get_adj_ability_bool(special, tag_name, dir, loc, from) && ability_affects_weapon(special, weapon, false) && ability_affects_weapon(special, opp_weapon, true));
1308 }
1309 
1310 bool attack_type::check_self_abilities(const config& cfg, const std::string& special) const
1311 {
1312  return check_self_abilities_impl(shared_from_this(), other_attack_, cfg, self_, self_loc_, AFFECT_SELF, special, true);
1313 }
1314 
1315 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)
1316 {
1317  if(tag_name == "leadership" && leader_bool){
1318  if((*u).get_self_ability_bool_weapon(special, tag_name, loc, self_attack, other_attack)) {
1319  return true;
1320  }
1321  }
1322  if((*u).checking_tags().count(tag_name) != 0){
1323  if((*u).get_self_ability_bool(special, tag_name, loc) && special_active_impl(self_attack, other_attack, special, whom, tag_name, true, "filter_student")) {
1324  return true;
1325  }
1326  }
1327  return false;
1328 }
1329 
1330 bool attack_type::check_adj_abilities(const config& cfg, const std::string& special, int dir, const unit& from) const
1331 {
1332  return check_adj_abilities_impl(shared_from_this(), other_attack_, cfg, self_, from, dir, self_loc_, AFFECT_SELF, special, true);
1333 }
1334 
1335 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)
1336 {
1337  if(tag_name == "leadership" && leader_bool){
1338  if((*u).get_adj_ability_bool_weapon(special, tag_name, dir, loc, from, self_attack, other_attack)) {
1339  return true;
1340  }
1341  }
1342  if((*u).checking_tags().count(tag_name) != 0){
1343  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")) {
1344  return true;
1345  }
1346  }
1347  return false;
1348 }
1349 /**
1350  * Returns whether or not @a *this has a special ability with a tag or id equal to
1351  * @a special. the Check is for a special ability
1352  * active in the current context (see set_specials_context), including
1353  * specials obtained from the opponent's attack.
1354  */
1355 bool attack_type::has_weapon_ability(const std::string& special, bool special_id, bool special_tags) const
1356 {
1357  assert(display::get_singleton());
1358  const unit_map& units = display::get_singleton()->get_units();
1359  if(self_){
1360  std::vector<special_match> special_tag_matches_self;
1361  std::vector<special_match> special_id_matches_self;
1362  get_ability_children(special_tag_matches_self, special_id_matches_self, (*self_).abilities(), special, special_id , special_tags);
1363  if(special_tags){
1364  for(const special_match& entry : special_tag_matches_self) {
1365  if(check_self_abilities(*entry.cfg, entry.tag_name)){
1366  return true;
1367  }
1368  }
1369  }
1370  if(special_id){
1371  for(const special_match& entry : special_id_matches_self) {
1372  if(check_self_abilities(*entry.cfg, entry.tag_name)){
1373  return true;
1374  }
1375  }
1376  }
1377 
1378  const auto adjacent = get_adjacent_tiles(self_loc_);
1379  for(unsigned i = 0; i < adjacent.size(); ++i) {
1380  const unit_map::const_iterator it = units.find(adjacent[i]);
1381  if (it == units.end() || it->incapacitated())
1382  continue;
1383  if ( &*it == self_.get() )
1384  continue;
1385 
1386  std::vector<special_match> special_tag_matches_adj;
1387  std::vector<special_match> special_id_matches_adj;
1388  get_ability_children(special_tag_matches_adj, special_id_matches_adj, it->abilities(), special, special_id , special_tags);
1389  if(special_tags){
1390  for(const special_match& entry : special_tag_matches_adj) {
1391  if(check_adj_abilities(*entry.cfg, entry.tag_name, i , *it)){
1392  return true;
1393  }
1394  }
1395  }
1396  if(special_id){
1397  for(const special_match& entry : special_id_matches_adj) {
1398  if(check_adj_abilities(*entry.cfg, entry.tag_name, i , *it)){
1399  return true;
1400  }
1401  }
1402  }
1403  }
1404  }
1405 
1406  if(other_){
1407  std::vector<special_match> special_tag_matches_other;
1408  std::vector<special_match> special_id_matches_other;
1409  get_ability_children(special_tag_matches_other, special_id_matches_other, (*other_).abilities(), special, special_id , special_tags);
1410  if(special_tags){
1411  for(const special_match& entry : special_tag_matches_other) {
1412  if(check_self_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, other_loc_, AFFECT_OTHER, entry.tag_name)){
1413  return true;
1414  }
1415  }
1416  }
1417 
1418  if(special_id){
1419  for(const special_match& entry : special_id_matches_other) {
1420  if(check_self_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, other_loc_, AFFECT_OTHER, entry.tag_name)){
1421  return true;
1422  }
1423  }
1424  }
1425 
1426  const auto adjacent = get_adjacent_tiles(other_loc_);
1427  for(unsigned i = 0; i < adjacent.size(); ++i) {
1428  const unit_map::const_iterator it = units.find(adjacent[i]);
1429  if (it == units.end() || it->incapacitated())
1430  continue;
1431  if ( &*it == other_.get() )
1432  continue;
1433 
1434  std::vector<special_match> special_tag_matches_oadj;
1435  std::vector<special_match> special_id_matches_oadj;
1436  get_ability_children(special_tag_matches_oadj, special_id_matches_oadj, it->abilities(), special, special_id , special_tags);
1437  if(special_tags){
1438  for(const special_match& entry : special_tag_matches_oadj) {
1439  if(check_adj_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *it, i, other_loc_, AFFECT_OTHER, entry.tag_name)){
1440  return true;
1441  }
1442  }
1443  }
1444 
1445  if(special_id){
1446  for(const special_match& entry : special_id_matches_oadj) {
1447  if(check_adj_abilities_impl(other_attack_, shared_from_this(), *entry.cfg, other_, *it, i, other_loc_, AFFECT_OTHER, entry.tag_name)){
1448  return true;
1449  }
1450  }
1451  }
1452  }
1453  }
1454  return false;
1455 }
1456 
1457 bool attack_type::has_special_or_ability(const std::string& special, bool special_id, bool special_tags) const
1458 {
1459  return (has_special(special, false, special_id, special_tags) || has_weapon_ability(special, special_id, special_tags));
1460 }
1461 //end of emulate weapon special functions.
1462 
1463 bool attack_type::special_active(const config& special, AFFECTS whom, const std::string& tag_name,
1464  bool include_backstab, const std::string& filter_self) const
1465 {
1466  return special_active_impl(shared_from_this(), other_attack_, special, whom, tag_name, include_backstab, filter_self);
1467 }
1468 
1469 /**
1470  * Returns whether or not the given special is active for the specified unit,
1471  * based on the current context (see set_specials_context).
1472  * @param self_attack this unit's attack
1473  * @param other_attack the other unit's attack
1474  * @param special a weapon special WML structure
1475  * @param whom specifies which combatant we care about
1476  * @param tag_name tag name of the special config
1477  * @param include_backstab false if backstab specials should not be active
1478  * (usually true since backstab is usually accounted
1479  * for elsewhere)
1480  * @param filter_self the filter to use
1481  */
1482 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,
1483  bool include_backstab, const std::string& filter_self)
1484 {
1485  assert(self_attack || other_attack);
1486  bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_;
1487  bool is_for_listing = self_attack ? self_attack->is_for_listing_ : other_attack->is_for_listing_;
1488  //log_scope("special_active");
1489 
1490  // Backstab check
1491  if ( !include_backstab )
1492  if ( special["backstab"].to_bool() )
1493  return false;
1494 
1495  // Does this affect the specified unit?
1496  if ( whom == AFFECT_SELF ) {
1497  if ( !special_affects_self(special, is_attacker) )
1498  return false;
1499  }
1500  if ( whom == AFFECT_OTHER ) {
1501  if ( !special_affects_opponent(special, is_attacker) )
1502  return false;
1503  }
1504 
1505  // Is this active on attack/defense?
1506  const std::string & active_on = special["active_on"];
1507  if ( !active_on.empty() ) {
1508  if ( is_attacker && active_on != "offense" )
1509  return false;
1510  if ( !is_attacker && active_on != "defense" )
1511  return false;
1512  }
1513 
1514  // Get the units involved.
1515  assert(display::get_singleton());
1516  const unit_map& units = display::get_singleton()->get_units();
1517 
1518  unit_const_ptr self = self_attack ? self_attack->self_ : other_attack->other_;
1519  unit_const_ptr other = self_attack ? self_attack->other_ : other_attack->self_;
1520  map_location self_loc = self_attack ? self_attack->self_loc_ : other_attack->other_loc_;
1521  map_location other_loc = self_attack ? self_attack->other_loc_ : other_attack->self_loc_;
1522  //TODO: why is this needed?
1523  if(self == nullptr) {
1524  unit_map::const_iterator it = units.find(self_loc);
1525  if(it.valid()) {
1526  self = it.get_shared_ptr();
1527  }
1528  }
1529  if(other == nullptr) {
1530  unit_map::const_iterator it = units.find(other_loc);
1531  if(it.valid()) {
1532  other = it.get_shared_ptr();
1533  }
1534  }
1535 
1536  // Make sure they're facing each other.
1537  temporary_facing self_facing(self, self_loc.get_relative_dir(other_loc));
1538  temporary_facing other_facing(other, other_loc.get_relative_dir(self_loc));
1539 
1540  // Filter poison, plague, drain, first strike
1541  if (tag_name == "drains" && other && other->get_state("undrainable")) {
1542  return false;
1543  }
1544  if (tag_name == "plague" && other &&
1545  (other->get_state("unplagueable") ||
1546  resources::gameboard->map().is_village(other_loc))) {
1547  return false;
1548  }
1549  if (tag_name == "poison" && other &&
1550  (other->get_state("unpoisonable") || other->get_state(unit::STATE_POISONED))) {
1551  return false;
1552  }
1553  if (tag_name == "firststrike" && !is_attacker && other_attack &&
1554  other_attack->has_special_or_ability("firststrike")) {
1555  return false;
1556  }
1557  if (tag_name == "slow" && other &&
1558  (other->get_state("unslowable") || other->get_state(unit::STATE_SLOWED))) {
1559  return false;
1560  }
1561  if (tag_name == "petrifies" && other &&
1562  other->get_state("unpetrifiable")) {
1563  return false;
1564  }
1565 
1566 
1567  // Translate our context into terms of "attacker" and "defender".
1568  unit_const_ptr & att = is_attacker ? self : other;
1569  unit_const_ptr & def = is_attacker ? other : self;
1570  const map_location & att_loc = is_attacker ? self_loc : other_loc;
1571  const map_location & def_loc = is_attacker ? other_loc : self_loc;
1572  const_attack_ptr att_weapon = is_attacker ? self_attack : other_attack;
1573  const_attack_ptr def_weapon = is_attacker ? other_attack : self_attack;
1574 
1575  // Filter the units involved.
1576  if (!special_unit_matches(self, other, self_loc, self_attack, special, is_for_listing, filter_self))
1577  return false;
1578  if (!special_unit_matches(other, self, other_loc, other_attack, special, is_for_listing, "filter_opponent"))
1579  return false;
1580  if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing, "filter_attacker"))
1581  return false;
1582  if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing, "filter_defender"))
1583  return false;
1584 
1585  const auto adjacent = get_adjacent_tiles(self_loc);
1586 
1587  // Filter the adjacent units.
1588  for (const config &i : special.child_range("filter_adjacent"))
1589  {
1590  std::size_t count = 0;
1591  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
1592  unit_filter filter{ vconfig(i) };
1593  for (const map_location::DIRECTION index : dirs)
1594  {
1596  continue;
1597  unit_map::const_iterator unit = units.find(adjacent[index]);
1598  if (unit == units.end() || !filter.matches(*unit, adjacent[index], *self))
1599  return false;
1600  if (i.has_attribute("is_enemy")) {
1602  if (i["is_enemy"].to_bool() != dc.get_team(unit->side()).is_enemy(self->side())) {
1603  continue;
1604  }
1605  }
1606  count++;
1607  }
1608  if (i["count"].empty() && count != dirs.size()) {
1609  return false;
1610  }
1611  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
1612  return false;
1613  }
1614  }
1615 
1616  // Filter the adjacent locations.
1617  for (const config &i : special.child_range("filter_adjacent_location"))
1618  {
1619  std::size_t count = 0;
1620  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
1621  terrain_filter adj_filter(vconfig(i), resources::filter_con, false);
1622  for (const map_location::DIRECTION index : dirs)
1623  {
1625  continue;
1626  if(!adj_filter.match(adjacent[index])) {
1627  return false;
1628  }
1629  count++;
1630  }
1631  if (i["count"].empty() && count != dirs.size()) {
1632  return false;
1633  }
1634  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
1635  return false;
1636  }
1637  }
1638 
1639  return true;
1640 }
1641 
1642 
1643 
1645 {
1646 
1647 void individual_effect::set(value_modifier t, int val, const config *abil, const map_location &l)
1648 {
1649  type=t;
1650  value=val;
1651  ability=abil;
1652  loc=l;
1653 }
1654 
1655 bool filter_base_matches(const config& cfg, int def)
1656 {
1657  if (const config &apply_filter = cfg.child("filter_base_value")) {
1658  config::attribute_value cond_eq = apply_filter["equals"];
1659  config::attribute_value cond_ne = apply_filter["not_equals"];
1660  config::attribute_value cond_lt = apply_filter["less_than"];
1661  config::attribute_value cond_gt = apply_filter["greater_than"];
1662  config::attribute_value cond_ge = apply_filter["greater_than_equal_to"];
1663  config::attribute_value cond_le = apply_filter["less_than_equal_to"];
1664  return (cond_eq.empty() || def == cond_eq.to_int()) &&
1665  (cond_ne.empty() || def != cond_ne.to_int()) &&
1666  (cond_lt.empty() || def < cond_lt.to_int()) &&
1667  (cond_gt.empty() || def > cond_gt.to_int()) &&
1668  (cond_ge.empty() || def >= cond_ge.to_int()) &&
1669  (cond_le.empty() || def <= cond_le.to_int());
1670  }
1671  return true;
1672 }
1673 
1674 effect::effect(const unit_ability_list& list, int def, bool backstab, const_attack_ptr att) :
1675  effect_list_(),
1676  composite_value_(0)
1677 {
1678 
1679  int value_set = def;
1680  std::map<std::string,individual_effect> values_add;
1681  std::map<std::string,individual_effect> values_mul;
1682  std::map<std::string,individual_effect> values_div;
1683 
1684  individual_effect set_effect_max;
1685  individual_effect set_effect_min;
1686 
1687  for (const unit_ability & ability : list) {
1688  const config& cfg = *ability.ability_cfg;
1689  const std::string& effect_id = cfg[cfg["id"].empty() ? "name" : "id"];
1690 
1691  if (!cfg["backstab"].blank()) {
1692  deprecated_message("backstab= in weapon specials", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use [filter_adjacent] instead.");
1693  }
1694 
1695  if (!backstab && cfg["backstab"].to_bool())
1696  continue;
1697  if (!filter_base_matches(cfg, def))
1698  continue;
1699 
1700  if (const config::attribute_value *v = cfg.get("value")) {
1701  int value = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1702  callable.add("base_value", wfl::variant(def));
1703  return formula.evaluate(callable).as_int();
1704  });
1705 
1706  int value_cum = cfg["cumulative"].to_bool() ? std::max(def, value) : value;
1707  assert((set_effect_min.type != NOT_USED) == (set_effect_max.type != NOT_USED));
1708  if(set_effect_min.type == NOT_USED) {
1709  set_effect_min.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1710  set_effect_max.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1711  }
1712  else {
1713  if(value_cum > set_effect_max.value) {
1714  set_effect_max.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1715  }
1716  if(value_cum < set_effect_min.value) {
1717  set_effect_min.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc);
1718  }
1719  }
1720  }
1721 
1722  if (const config::attribute_value *v = cfg.get("add")) {
1723  int add = get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1724  callable.add("base_value", wfl::variant(def));
1725  return formula.evaluate(callable).as_int();
1726  });
1727  std::map<std::string,individual_effect>::iterator add_effect = values_add.find(effect_id);
1728  if(add_effect == values_add.end() || add > add_effect->second.value) {
1729  values_add[effect_id].set(ADD, add, ability.ability_cfg, ability.teacher_loc);
1730  }
1731  }
1732  if (const config::attribute_value *v = cfg.get("sub")) {
1733  int sub = - get_single_ability_value(*v, def, ability, list.loc(), att, [&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1734  callable.add("base_value", wfl::variant(def));
1735  return formula.evaluate(callable).as_int();
1736  });
1737  std::map<std::string,individual_effect>::iterator sub_effect = values_add.find(effect_id);
1738  if(sub_effect == values_add.end() || sub < sub_effect->second.value) {
1739  values_add[effect_id].set(ADD, sub, ability.ability_cfg, ability.teacher_loc);
1740  }
1741  }
1742  if (const config::attribute_value *v = cfg.get("multiply")) {
1743  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) {
1744  callable.add("base_value", wfl::variant(def));
1745  return formula.evaluate(callable).as_decimal() / 1000.0 ;
1746  }) * 100);
1747  std::map<std::string,individual_effect>::iterator mul_effect = values_mul.find(effect_id);
1748  if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
1749  values_mul[effect_id].set(MUL, multiply, ability.ability_cfg, ability.teacher_loc);
1750  }
1751  }
1752  if (const config::attribute_value *v = cfg.get("divide")) {
1753  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) {
1754  callable.add("base_value", wfl::variant(def));
1755  return formula.evaluate(callable).as_decimal() / 1000.0 ;
1756  }) * 100);
1757 
1758  if (divide == 0) {
1759  ERR_NG << "division by zero with divide= in ability/weapon special " << effect_id << std::endl;
1760  }
1761  else {
1762  std::map<std::string,individual_effect>::iterator div_effect = values_div.find(effect_id);
1763  if(div_effect == values_div.end() || divide > div_effect->second.value) {
1764  values_div[effect_id].set(DIV, divide, ability.ability_cfg, ability.teacher_loc);
1765  }
1766  }
1767  }
1768  }
1769 
1770  if(set_effect_max.type != NOT_USED) {
1771  value_set = std::max(set_effect_max.value, 0) + std::min(set_effect_min.value, 0);
1772  if(set_effect_max.value > def) {
1773  effect_list_.push_back(set_effect_max);
1774  }
1775  if(set_effect_min.value < def) {
1776  effect_list_.push_back(set_effect_min);
1777  }
1778  }
1779 
1780  /* Do multiplication with floating point values rather than integers
1781  * We want two places of precision for each multiplier
1782  * Using integers multiplied by 100 to keep precision causes overflow
1783  * after 3-4 abilities for 32-bit values and ~8 for 64-bit
1784  * Avoiding the overflow by dividing after each step introduces rounding errors
1785  * that may vary depending on the order effects are applied
1786  * As the final values are likely <1000 (always true for mainline), loss of less significant digits is not an issue
1787  */
1788  double multiplier = 1.0;
1789  double divisor = 1.0;
1790 
1791  for(const auto& val : values_mul) {
1792  multiplier *= val.second.value/100.0;
1793  effect_list_.push_back(val.second);
1794  }
1795 
1796  for(const auto& val : values_div) {
1797  divisor *= val.second.value/100.0;
1798  effect_list_.push_back(val.second);
1799  }
1800 
1801  int addition = 0;
1802  for(const auto& val : values_add) {
1803  addition += val.second.value;
1804  effect_list_.push_back(val.second);
1805  }
1806 
1807  composite_value_ = static_cast<int>((value_set + addition) * multiplier / divisor);
1808 }
1809 
1810 } // end namespace unit_abilities
const color_t BUTTON_COLOR
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:867
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:1463
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.
const color_t GOOD_COLOR
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:1289
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:1647
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:1335
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:1020
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:1294
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
int num_attacks() const
Definition: attack_type.hpp:54
static void add_name(std::string &weapon_abilities, bool active, const config::any_child sp, std::set< std::string > &checking_name, bool affect_adjacent)
Definition: abilities.cpp:842
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:1655
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:1310
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:988
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:1457
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:1221
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:1300
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:1355
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:1305
unit_ability_list get_weapon_ability(const std::string &ability) const
Returns list for weapon like abilities for each ability type.
Definition: abilities.cpp:1167
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:1047
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:1276
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:1482
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:1330
static bool overwrite_special_affects(const config &special)
Definition: abilities.cpp:1215
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:1201
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:1315
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
const color_t BAD_COLOR
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:289