The Battle for Wesnoth  1.15.0-dev
abilities.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Dominic Bolin <dominic.bolin@exong.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Manage unit-abilities, like heal, cure, and weapon_specials.
18  */
19 
20 #include "display.hpp"
21 #include "display_context.hpp"
22 #include "game_board.hpp"
23 #include "lexical_cast.hpp"
24 #include "log.hpp"
25 #include "resources.hpp"
26 #include "team.hpp"
27 #include "terrain/filter.hpp"
28 #include "units/unit.hpp"
29 #include "units/abilities.hpp"
30 #include "units/filter.hpp"
31 #include "units/map.hpp"
32 #include "filter_context.hpp"
33 #include "formula/callable_objects.hpp"
34 #include "formula/formula.hpp"
37 #include "deprecation.hpp"
38 
39 #include <boost/dynamic_bitset.hpp>
40 #include <boost/algorithm/string/predicate.hpp>
41 
42 static lg::log_domain log_engine("engine");
43 #define ERR_NG LOG_STREAM(err, log_engine)
44 
45 namespace {
46  class temporary_facing
47  {
48  map_location::DIRECTION save_dir_;
49  unit_const_ptr u_;
50  public:
51  temporary_facing(unit_const_ptr u, map_location::DIRECTION new_dir)
52  : save_dir_(u ? u->facing() : map_location::NDIRECTIONS)
53  , u_(u)
54  {
55  if (u_) {
56  u_->set_facing(new_dir);
57  }
58  }
59  ~temporary_facing()
60  {
61  if (u_) {
62  u_->set_facing(save_dir_);
63  }
64  }
65  };
66 }
67 
68 /*
69  *
70  * [abilities]
71  * ...
72  *
73  * [heals]
74  * value=4
75  * max_value=8
76  * cumulative=no
77  * affect_allies=yes
78  * name= _ "heals"
79  * female_name= _ "female^heals"
80  * name_inactive=null
81  * female_name_inactive=null
82  * description= _ "Heals:
83 Allows the unit to heal adjacent friendly units at the beginning of each turn.
84 
85 A unit cared for by a healer may heal up to 4 HP per turn.
86 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."
87  * description_inactive=null
88  *
89  * affect_self=yes
90  * [filter] // SUF
91  * ...
92  * [/filter]
93  * [filter_self] // SUF
94  * ...
95  * [/filter_self]
96  * [filter_adjacent] // SUF
97  * adjacent=n,ne,nw
98  * ...
99  * [/filter_adjacent]
100  * [filter_adjacent_location]
101  * adjacent=n,ne,nw
102  * ...
103  * [/filter_adjacent]
104  * [affect_adjacent]
105  * adjacent=n,ne,nw
106  * [filter] // SUF
107  * ...
108  * [/filter]
109  * [/affect_adjacent]
110  * [affect_adjacent]
111  * adjacent=s,se,sw
112  * [filter] // SUF
113  * ...
114  * [/filter]
115  * [/affect_adjacent]
116  *
117  * [/heals]
118  *
119  * ...
120  * [/abilities]
121  *
122  */
123 
124 
125 namespace {
126 
127 bool affects_side(const config& cfg, std::size_t side, std::size_t other_side)
128 {
129  // display::get_singleton() has already been confirmed valid by both callers.
130  const team& side_team = display::get_singleton()->get_disp_context().get_team(side);
131 
132  if(side == other_side || !side_team.is_enemy(other_side)) {
133  return cfg["affect_allies"].to_bool(true);
134  } else {
135  return cfg["affect_enemies"].to_bool();
136  }
137 }
138 
139 }
140 
141 bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc) const
142 {
143  for (const config &i : this->abilities_.child_range(tag_name)) {
144  if (ability_active(tag_name, i, loc) &&
145  ability_affects_self(tag_name, i, loc))
146  {
147  return true;
148  }
149  }
150 
151  assert(display::get_singleton());
152  const unit_map& units = display::get_singleton()->get_units();
153 
154  adjacent_loc_array_t adjacent;
155  get_adjacent_tiles(loc,adjacent.data());
156  for(unsigned i = 0; i < adjacent.size(); ++i) {
157  const unit_map::const_iterator it = units.find(adjacent[i]);
158  if (it == units.end() || it->incapacitated())
159  continue;
160  // Abilities may be tested at locations other than the unit's current
161  // location. This is intentional to allow for less messing with the unit
162  // map during calculations, particularly with regards to movement.
163  // Thus, we need to make sure the adjacent unit (*it) is not actually
164  // ourself.
165  if ( &*it == this )
166  continue;
167  for (const config &j : it->abilities_.child_range(tag_name)) {
168  if (affects_side(j, side(), it->side()) &&
169  it->ability_active(tag_name, j, adjacent[i]) &&
170  ability_affects_adjacent(tag_name, j, i, loc, *it))
171  {
172  return true;
173  }
174  }
175  }
176 
177 
178  return false;
179 }
180 
181 unit_ability_list unit::get_abilities(const std::string& tag_name, const map_location& loc, const_attack_ptr weapon, const_attack_ptr opp_weapon) const
182 {
183  unit_ability_list res(loc_);
184 
185  for(const config& i : this->abilities_.child_range(tag_name)) {
186  if(ability_active(tag_name, i, loc)
187  && ability_affects_self(tag_name, i, loc)
188  && ability_affects_weapon(i, weapon, false)
189  && ability_affects_weapon(i, opp_weapon, true)
190  ) {
191  res.emplace_back(&i, loc);
192  }
193  }
194 
195  assert(display::get_singleton());
196  const unit_map& units = display::get_singleton()->get_units();
197 
198  adjacent_loc_array_t adjacent;
199  get_adjacent_tiles(loc,adjacent.data());
200  for(unsigned i = 0; i < adjacent.size(); ++i) {
201  const unit_map::const_iterator it = units.find(adjacent[i]);
202  if (it == units.end() || it->incapacitated())
203  continue;
204  // Abilities may be tested at locations other than the unit's current
205  // location. This is intentional to allow for less messing with the unit
206  // map during calculations, particularly with regards to movement.
207  // Thus, we need to make sure the adjacent unit (*it) is not actually
208  // ourself.
209  if ( &*it == this )
210  continue;
211  for(const config& j : it->abilities_.child_range(tag_name)) {
212  if(affects_side(j, side(), it->side())
213  && it->ability_active(tag_name, j, adjacent[i])
214  && ability_affects_adjacent(tag_name, j, i, loc, *it) && ability_affects_weapon(j, weapon, false)
215  && ability_affects_weapon(j, opp_weapon, true)
216  ) {
217  res.emplace_back(&j, adjacent[i]);
218  }
219  }
220  }
221 
222 
223  return res;
224 }
225 
226 std::vector<std::string> unit::get_ability_list() const
227 {
228  std::vector<std::string> res;
229 
230  for (const config::any_child &ab : this->abilities_.all_children_range()) {
231  std::string id = ab.cfg["id"];
232  if (!id.empty())
233  res.push_back(std::move(id));
234  }
235  return res;
236 }
237 
238 
239 namespace {
240  // These functions might have wider usefulness than this file, but for now
241  // I'll make them local.
242 
243  /**
244  * Chooses a value from the given config. If the value specified by @a key is
245  * blank, then @a default_key is chosen instead.
246  */
247  inline const config::attribute_value & default_value(
248  const config & cfg, const std::string & key, const std::string & default_key)
249  {
250  const config::attribute_value & value = cfg[key];
251  return !value.blank() ? value : cfg[default_key];
252  }
253 
254  /**
255  * Chooses a value from the given config based on gender. If the value for
256  * the specified gender is blank, then @a default_key is chosen instead.
257  */
258  inline const config::attribute_value & gender_value(
259  const config & cfg, unit_race::GENDER gender, const std::string & male_key,
260  const std::string & female_key, const std::string & default_key)
261  {
262  return default_value(cfg,
263  gender == unit_race::MALE ? male_key : female_key,
264  default_key);
265  }
266 }
267 
268 std::vector<std::tuple<std::string, t_string, t_string, t_string>> unit::ability_tooltips(boost::dynamic_bitset<>* active_list) const
269 {
270  std::vector<std::tuple<std::string, t_string,t_string,t_string>> res;
271  if ( active_list )
272  active_list->clear();
273 
274  for (const config::any_child &ab : this->abilities_.all_children_range())
275  {
276  if ( !active_list || ability_active(ab.key, ab.cfg, loc_) )
277  {
278  const t_string& name =
279  gender_value(ab.cfg, gender_, "name", "female_name", "name").t_str();
280 
281  if (!name.empty()) {
282  res.emplace_back(
283  ab.cfg["id"],
284  ab.cfg["name"].t_str(),
285  name,
286  ab.cfg["description"].t_str() );
287  if ( active_list )
288  active_list->push_back(true);
289  }
290  }
291  else
292  {
293  // See if an inactive name was specified.
294  const config::attribute_value& inactive_value =
295  gender_value(ab.cfg, gender_, "name_inactive",
296  "female_name_inactive", "name_inactive");
297  const t_string& name = !inactive_value.blank() ? inactive_value.t_str() :
298  gender_value(ab.cfg, gender_, "name", "female_name", "name").t_str();
299 
300  if (!name.empty()) {
301  res.emplace_back(
302  ab.cfg["id"],
303  default_value(ab.cfg, "name_inactive", "name").t_str(),
304  name,
305  default_value(ab.cfg, "description_inactive", "description").t_str() );
306  active_list->push_back(false);
307  }
308  }
309  }
310  return res;
311 }
312 
313 bool unit::ability_active(const std::string& ability,const config& cfg,const map_location& loc) const
314 {
315  bool illuminates = ability == "illuminates";
316 
317  if (const config &afilter = cfg.child("filter"))
318  if ( !unit_filter(vconfig(afilter)).set_use_flat_tod(illuminates).matches(*this, loc) )
319  return false;
320 
321  adjacent_loc_array_t adjacent;
322  get_adjacent_tiles(loc,adjacent.data());
323 
324  assert(display::get_singleton());
325  const unit_map& units = display::get_singleton()->get_units();
326 
327  for (const config &i : cfg.child_range("filter_adjacent"))
328  {
329  std::size_t count = 0;
330  unit_filter ufilt{ vconfig(i) };
331  ufilt.set_use_flat_tod(illuminates);
332  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
333  for (const map_location::DIRECTION index : dirs)
334  {
336  continue;
337  unit_map::const_iterator unit = units.find(adjacent[index]);
338  if (unit == units.end())
339  return false;
340  if (!ufilt(*unit, *this))
341  return false;
342  if (i.has_attribute("is_enemy")) {
344  if (i["is_enemy"].to_bool() != dc.get_team(unit->side()).is_enemy(side_)) {
345  continue;
346  }
347  }
348  count++;
349  }
350  if (i["count"].empty() && count != dirs.size()) {
351  return false;
352  }
353  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
354  return false;
355  }
356  }
357 
358  for (const config &i : cfg.child_range("filter_adjacent_location"))
359  {
360  std::size_t count = 0;
361  terrain_filter adj_filter(vconfig(i), resources::filter_con);
362  adj_filter.flatten(illuminates);
363 
364  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
365  for (const map_location::DIRECTION index : dirs)
366  {
368  continue;
369  }
370  if(!adj_filter.match(adjacent[index])) {
371  return false;
372  }
373  count++;
374  }
375  if (i["count"].empty() && count != dirs.size()) {
376  return false;
377  }
378  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
379  return false;
380  }
381  }
382  return true;
383 }
384 
385 bool unit::ability_affects_adjacent(const std::string& ability, const config& cfg,int dir,const map_location& loc,const unit& from) const
386 {
387  bool illuminates = ability == "illuminates";
388 
389  assert(dir >=0 && dir <= 5);
390  map_location::DIRECTION direction = static_cast<map_location::DIRECTION>(dir);
391 
392  for (const config &i : cfg.child_range("affect_adjacent"))
393  {
394  if (i.has_attribute("adjacent")) { //key adjacent defined
395  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
396  if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
397  continue;
398  }
399  }
400  const config &filter = i.child("filter");
401  if (!filter || //filter tag given
402  unit_filter(vconfig(filter)).set_use_flat_tod(illuminates).matches(*this, loc, from) ) {
403  return true;
404  }
405  }
406  return false;
407 }
408 
409 bool unit::ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const
410 {
411  const config &filter = cfg.child("filter_self");
412  bool affect_self = cfg["affect_self"].to_bool(true);
413  if (!filter || !affect_self) return affect_self;
414  return unit_filter(vconfig(filter)).set_use_flat_tod(ability == "illuminates").matches(*this, loc);
415 }
416 
417 bool unit::ability_affects_weapon(const config& cfg, const_attack_ptr weapon, bool is_opp) const
418 {
419  const std::string filter_tag_name = is_opp ? "filter_second_weapon" : "filter_weapon";
420  if(!cfg.has_child(filter_tag_name)) {
421  return true;
422  }
423  const config& filter = cfg.child(filter_tag_name);
424  if(!weapon) {
425  return false;
426  }
427  return weapon->matches_filter(filter);
428 }
429 
430 bool unit::has_ability_type(const std::string& ability) const
431 {
432  return !abilities_.child_range(ability).empty();
433 }
434 
435 namespace {
436 
437 
438 template<typename T, typename TFuncFormula>
439 class get_ability_value_visitor : public boost::static_visitor<T>
440 {
441 public:
442  // Constructor stores the default value.
443  get_ability_value_visitor(T def, const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
444 
445  T operator()(const boost::blank&) const { return def_; }
446  T operator()(bool) const { return def_; }
447  T operator()(int i) const { return static_cast<T>(i); }
448  T operator()(unsigned long long u) const { return static_cast<T>(u); }
449  T operator()(double d) const { return static_cast<T>(d); }
450  T operator()(const t_string&) const { return def_; }
451  T operator()(const std::string& s) const
452  {
453  if(s.size() >= 2 && s[0] == '(') {
454  return formula_handler_(s);
455  }
456  return lexical_cast_default<T>(s, def_);
457  }
458 
459 private:
460  const T def_;
461  const TFuncFormula& formula_handler_;
462 };
463 template<typename T, typename TFuncFormula>
464 get_ability_value_visitor<T, TFuncFormula> make_get_ability_value_visitor(T def, const TFuncFormula& formula_handler)
465 {
466  return get_ability_value_visitor<T, TFuncFormula>(def, formula_handler);
467 }
468 template<typename T, typename TFuncFormula>
469 T get_single_ability_value(const config::attribute_value& v, T def, const map_location& sender_loc, const map_location& receiver_loc, const TFuncFormula& formula_handler)
470 {
471  return v.apply_visitor(make_get_ability_value_visitor(def, [&](const std::string& s) {
472 
473  try {
474  assert(display::get_singleton());
475  const unit_map& units = display::get_singleton()->get_units();
476 
477  auto u_itor = units.find(sender_loc);
478 
479  if(u_itor == units.end()) {
480  return def;
481  }
482  wfl::map_formula_callable callable(std::make_shared<wfl::unit_callable>(*u_itor));
483  u_itor = units.find(receiver_loc);
484  if(u_itor != units.end()) {
485  callable.add("other", wfl::variant(std::make_shared<wfl::unit_callable>(*u_itor)));
486  }
487  return formula_handler(wfl::formula(s, new wfl::gamestate_function_symbol_table), callable);
488  } catch(const wfl::formula_error& e) {
489  lg::wml_error() << "Formula error in ability or weapon special: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
490  return def;
491  }
492  }));
493 }
494 }
495 
496 template<typename TComp>
497 std::pair<int,map_location> unit_ability_list::get_extremum(const std::string& key, int def, const TComp& comp) const
498 {
499  if ( cfgs_.empty() ) {
500  return std::make_pair(def, map_location());
501  }
502  // The returned location is the best non-cumulative one, if any,
503  // the best absolute cumulative one otherwise.
504  map_location best_loc;
505  bool only_cumulative = true;
506  int abs_max = 0;
507  int flat = 0;
508  int stack = 0;
509  for (const unit_ability& p : cfgs_)
510  {
511  int value = get_single_ability_value((*p.first)[key], def, p.second, loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
512  return formula.evaluate(callable).as_int();
513  });
514 
515  if ((*p.first)["cumulative"].to_bool()) {
516  stack += value;
517  if (value < 0) value = -value;
518  if (only_cumulative && !comp(value, abs_max)) {
519  abs_max = value;
520  best_loc = p.second;
521  }
522  } else if (only_cumulative || comp(flat, value)) {
523  only_cumulative = false;
524  flat = value;
525  best_loc = p.second;
526  }
527  }
528  return std::make_pair(flat + stack, best_loc);
529 }
530 
531 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;
532 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;
533 
534 /*
535  *
536  * [special]
537  * [swarm]
538  * name= _ "swarm"
539  * name_inactive= _ ""
540  * description= _ ""
541  * description_inactive= _ ""
542  * cumulative=no
543  * apply_to=self #self,opponent,defender,attacker,both
544  * #active_on=defense # or offense; omitting this means "both"
545  *
546  * swarm_attacks_max=4
547  * swarm_attacks_min=2
548  *
549  * [filter_self] // SUF
550  * ...
551  * [/filter_self]
552  * [filter_opponent] // SUF
553  * [filter_attacker] // SUF
554  * [filter_defender] // SUF
555  * [filter_adjacent] // SAUF
556  * [filter_adjacent_location] // SAUF + locs
557  * [/swarm]
558  * [/special]
559  *
560  */
561 
562 namespace {
563 
564  /**
565  * Gets the children of @parent (which should be the specials for an
566  * attack_type) and places the ones whose tag or id= matches @a id into
567  * @a result.
568  * If @a just_peeking is set to true, then @a result is not touched;
569  * instead the return value is used to indicate if any matching children
570  * were found.
571  *
572  * @returns true if @a just_peeking is true and a match was found;
573  * false otherwise.
574  */
575  bool get_special_children(std::vector<const config*>& result, const config& parent,
576  const std::string& id, bool just_peeking=false) {
577  for (const config::any_child &sp : parent.all_children_range())
578  {
579  if (sp.key == id || sp.cfg["id"] == id) {
580  if(just_peeking) {
581  return true; // peek succeeded; done
582  } else {
583  result.push_back(&sp.cfg);
584  }
585  }
586  }
587  return false;
588  }
589 }
590 
591 /**
592  * Returns whether or not @a *this has a special with a tag or id equal to
593  * @a special. If @a simple_check is set to true, then the check is merely
594  * for being present. Otherwise (the default), the check is for a special
595  * active in the current context (see set_specials_context), including
596  * specials obtained from the opponent's attack.
597  */
598 bool attack_type::get_special_bool(const std::string& special, bool simple_check) const
599 {
600  {
601  std::vector<const config*> list;
602  if ( get_special_children(list, specials_, special, simple_check) ) {
603  return true;
604  }
605  // If we make it to here, then either list.empty() or !simple_check.
606  // So if the list is not empty, then this is not a simple check and
607  // we need to check each special in the list to see if any are active.
608  for(const config* entry : list) {
609  if ( special_active(*entry, AFFECT_SELF) ) {
610  return true;
611  }
612  }
613  }
614  // Skip checking the opponent's attack?
615  if ( simple_check || !other_attack_ ) {
616  return false;
617  }
618 
619  std::vector<const config*> list;
620  get_special_children(list, other_attack_->specials_, special);
621  for(const config* entry : list) {
622  if ( other_attack_->special_active(*entry, AFFECT_OTHER) ) {
623  return true;
624  }
625  }
626  return false;
627 }
628 
629 /**
630  * Returns the currently active specials as an ability list, given the current
631  * context (see set_specials_context).
632  */
633 unit_ability_list attack_type::get_specials(const std::string& special) const
634 {
635  //log_scope("get_specials");
636  unit_ability_list res(self_loc_);
637 
638  for(const config& i : specials_.child_range(special)) {
639  if(special_active(i, AFFECT_SELF)) {
640  res.emplace_back(&i, self_loc_);
641  }
642  }
643 
644  if(!other_attack_) {
645  return res;
646  }
647 
648  for(const config& i : other_attack_->specials_.child_range(special)) {
649  if(other_attack_->special_active(i, AFFECT_OTHER)) {
650  res.emplace_back(&i, other_loc_);
651  }
652  }
653  return res;
654 }
655 
656 /**
657  * Returns a vector of names and descriptions for the specials of *this.
658  * Each std::pair in the vector has first = name and second = description.
659  *
660  * This uses either the active or inactive name/description for each special,
661  * based on the current context (see set_specials_context), provided
662  * @a active_list is not nullptr. Otherwise specials are assumed active.
663  * If the appropriate name is empty, the special is skipped.
664  */
665 std::vector<std::pair<t_string, t_string>> attack_type::special_tooltips(
666  boost::dynamic_bitset<>* active_list) const
667 {
668  //log_scope("special_tooltips");
669  std::vector<std::pair<t_string, t_string>> res;
670  if ( active_list )
671  active_list->clear();
672 
673  for (const config::any_child &sp : specials_.all_children_range())
674  {
675  if ( !active_list || special_active(sp.cfg, AFFECT_EITHER) ) {
676  const t_string &name = sp.cfg["name"];
677  if (!name.empty()) {
678  res.emplace_back(name, sp.cfg["description"].t_str() );
679  if ( active_list )
680  active_list->push_back(true);
681  }
682  } else {
683  const t_string& name = default_value(sp.cfg, "name_inactive", "name").t_str();
684  if (!name.empty()) {
685  res.emplace_back(name, default_value(sp.cfg, "description_inactive", "description").t_str() );
686  active_list->push_back(false);
687  }
688  }
689  }
690  return res;
691 }
692 
693 /**
694  * Returns a comma-separated string of active names for the specials of *this.
695  * Empty names are skipped.
696  *
697  * This excludes inactive specials if only_active is true. Whether or not a
698  * special is active depends on the current context (see set_specials_context)
699  * and the @a is_backstab parameter.
700  */
701 std::string attack_type::weapon_specials(bool only_active, bool is_backstab) const
702 {
703  //log_scope("weapon_specials");
704  std::string res;
705  for (const config::any_child &sp : specials_.all_children_range())
706  {
707  if ( only_active && !special_active(sp.cfg, AFFECT_EITHER, is_backstab) )
708  continue;
709 
710  const std::string& name = sp.cfg["name"].str();
711  if (!name.empty()) {
712  if (!res.empty()) res += ", ";
713  res += name;
714  }
715  }
716 
717  return res;
718 }
719 
720 
721 /**
722  * Sets the context under which specials will be checked for being active.
723  * This version is appropriate if both units in a combat are known.
724  * @param[in] self A reference to the unit with this weapon.
725  * @param[in] other A reference to the other unit in the combat.
726  * @param[in] unit_loc The location of the unit with this weapon.
727  * @param[in] other_loc The location of the other unit in the combat.
728  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
729  * @param[in] other_attack The attack used by the other unit.
730  */
732  const_attack_ptr other_attack,
733  unit_const_ptr self,
734  unit_const_ptr other,
735  const map_location& unit_loc,
736  const map_location& other_loc,
737  bool attacking)
738  : parent(weapon.shared_from_this())
739 {
740  weapon.self_ = self;
741  weapon.other_ = other;
742  weapon.self_loc_ = unit_loc;
743  weapon.other_loc_ = other_loc;
744  weapon.is_attacker_ = attacking;
745  weapon.other_attack_ = other_attack;
746  weapon.is_for_listing_ = false;
747 }
748 
749 /**
750  * Sets the context under which specials will be checked for being active.
751  * This version is appropriate if there is no specific combat being considered.
752  * @param[in] self A reference to the unit with this weapon.
753  * @param[in] loc The location of the unit with this weapon.
754  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
755  */
757  : parent(weapon.shared_from_this())
758 {
759  weapon.self_ = self;
760  weapon.other_ = nullptr;
761  weapon.self_loc_ = loc;
763  weapon.is_attacker_ = attacking;
764  weapon.other_attack_ = nullptr;
765  weapon.is_for_listing_ = false;
766 }
767 
768 /**
769  * Sets the context under which specials will be checked for being active.
770  * This version is appropriate for theoretical units of a particular type.
771  * @param[in] self_type A reference to the type of the unit with this weapon.
772  * @param[in] loc The location of the unit with this weapon.
773  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
774  */
775 attack_type::specials_context_t::specials_context_t(const attack_type& weapon, const unit_type& self_type, const map_location& loc, bool attacking)
776  : parent(weapon.shared_from_this())
777 {
778  UNUSED(self_type);
779  weapon.self_ = nullptr;
780  weapon.other_ = nullptr;
781  weapon.self_loc_ = loc;
783  weapon.is_attacker_ = attacking;
784  weapon.other_attack_ = nullptr;
785  weapon.is_for_listing_ = false;
786 }
787 
789  : parent(weapon.shared_from_this())
790 {
791  weapon.is_for_listing_ = true;
792  weapon.is_attacker_ = attacking;
793 }
794 
796 {
797  if(was_moved) return;
798  parent->self_ = nullptr;
799  parent->other_ = nullptr;
800  parent->self_loc_ = map_location::null_location();
801  parent->other_loc_ = map_location::null_location();
802  parent->is_attacker_ = false;
803  parent->other_attack_ = nullptr;
804  parent->is_for_listing_ = false;
805 }
806 
808  : parent(other.parent)
809 {
810  other.was_moved = true;
811 }
812 
813 /**
814  * Calculates the number of attacks this weapon has, considering specials.
815  * This returns two numbers because of the swarm special. The actual number of
816  * attacks depends on the unit's health and should be:
817  * min_attacks + (max_attacks - min_attacks) * (current hp) / (max hp)
818  * c.f. swarm_blows()
819  */
820 void attack_type::modified_attacks(bool is_backstab, unsigned & min_attacks,
821  unsigned & max_attacks) const
822 {
823  // Apply [attacks].
824  unit_abilities::effect attacks_effect(get_specials("attacks"),
825  num_attacks(), is_backstab);
826  int attacks_value = attacks_effect.get_composite_value();
827  if ( combat_ability("attacks", attacks_value, is_backstab).second ) {
828  attacks_value = combat_ability("attacks", attacks_value, is_backstab).first;
829  }
830 
831  if ( attacks_value < 0 ) {
832  attacks_value = num_attacks();
833  ERR_NG << "negative number of strikes after applying weapon specials" << std::endl;
834  }
835 
836  // Apply [swarm].
837  unit_ability_list swarm_specials = get_specials("swarm");
838  if ( !swarm_specials.empty() ) {
839  min_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_min").first);
840  max_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_max", attacks_value).first);
841  } else {
842  min_attacks = max_attacks = attacks_value;
843  }
844 }
845 
846 
847 /**
848  * Returns the damage per attack of this weapon, considering specials.
849  */
850 int attack_type::modified_damage(bool is_backstab) const
851 {
852  unit_abilities::effect dmg_effect(get_specials("damage"), damage(), is_backstab);
853  int damage_value = dmg_effect.get_composite_value();
854  if ( combat_ability("damage", damage_value, is_backstab).second ) {
855  damage_value = combat_ability("damage", damage_value, is_backstab).first;
856  }
857  return damage_value;
858 }
859 
860 
861 namespace { // Helpers for attack_type::special_active()
862 
863  /**
864  * Returns whether or not the given special affects the opponent of the unit
865  * with the special.
866  * @param[in] special a weapon special WML structure
867  * @param[in] is_attacker whether or not the unit with the special is the attacker
868  */
869  bool special_affects_opponent(const config& special, bool is_attacker)
870  {
871  //log_scope("special_affects_opponent");
872  const std::string& apply_to = special["apply_to"];
873  if ( apply_to.empty() )
874  return false;
875  if ( apply_to == "both" )
876  return true;
877  if ( apply_to == "opponent" )
878  return true;
879  if ( is_attacker && apply_to == "defender" )
880  return true;
881  if ( !is_attacker && apply_to == "attacker" )
882  return true;
883  return false;
884  }
885 
886  /**
887  * Returns whether or not the given special affects the unit with the special.
888  * @param[in] special a weapon special WML structure
889  * @param[in] is_attacker whether or not the unit with the special is the attacker
890  */
891  bool special_affects_self(const config& special, bool is_attacker)
892  {
893  //log_scope("special_affects_self");
894  const std::string& apply_to = special["apply_to"];
895  if ( apply_to.empty() )
896  return true;
897  if ( apply_to == "both" )
898  return true;
899  if ( apply_to == "self" )
900  return true;
901  if ( is_attacker && apply_to == "attacker" )
902  return true;
903  if ( !is_attacker && apply_to == "defender")
904  return true;
905  return false;
906  }
907 
908  /**
909  * Determines if a unit/weapon combination matches the specified child
910  * (normally a [filter_*] child) of the provided filter.
911  * @param[in] u A unit to filter.
912  * @param[in] u2 Another unit to filter.
913  * @param[in] loc The presumed location of @a un_it.
914  * @param[in] weapon The attack_type to filter.
915  * @param[in] filter The filter containing the child filter to use.
916  * @param[in] child_tag The tag of the child filter to use.
917  */
918  static bool special_unit_matches(unit_const_ptr & u,
919  unit_const_ptr & u2,
920  const map_location & loc,
921  const_attack_ptr weapon,
922  const config & filter,
923  const bool for_listing,
924  const std::string & child_tag)
925  {
926  if (for_listing && !loc.valid())
927  // The special's context was set to ignore this unit, so assume we pass.
928  // (This is used by reports.cpp to show active specials when the
929  // opponent is not known. From a player's perspective, the special
930  // is active, in that it can be used, even though the player might
931  // need to select an appropriate opponent.)
932  return true;
933 
934  const config & filter_child = filter.child(child_tag);
935  if ( !filter_child )
936  // The special does not filter on this unit, so we pass.
937  return true;
938 
939  // If the primary unit doesn't exist, there's nothing to match
940  if (!u) {
941  return false;
942  }
943 
944  unit_filter ufilt{vconfig(filter_child)};
945 
946  // If the other unit doesn't exist, try matching without it
947  if (!u2) {
948  return ufilt.matches(*u, loc);
949  }
950 
951  // Check for a unit match.
952  if (!ufilt.matches(*u, loc, *u2)) {
953  return false;
954  }
955 
956  // Check for a weapon match.
957  if ( const config & filter_weapon = filter_child.child("filter_weapon") ) {
958  if ( !weapon || !weapon->matches_filter(filter_weapon) )
959  return false;
960  }
961 
962  // Passed.
963  return true;
964  }
965 
966 }//anonymous namespace
967 
968 /**
969  * Returns whether or not the given special is active for the specified unit,
970  * based on the current context (see set_specials_context).
971  * @param[in] special a weapon special WML structure
972  * @param[in] whom specifies which combatant we care about
973  * @param[in] include_backstab false if backstab specials should not be active
974  * (usually true since backstab is usually accounted
975  * for elsewhere)
976  */
977 bool attack_type::special_active(const config& special, AFFECTS whom,
978  bool include_backstab) const
979 {
980  //log_scope("special_active");
981 
982  // Backstab check
983  if ( !include_backstab )
984  if ( special["backstab"].to_bool() )
985  return false;
986 
987  // Does this affect the specified unit?
988  if ( whom == AFFECT_SELF ) {
989  if ( !special_affects_self(special, is_attacker_) )
990  return false;
991  }
992  if ( whom == AFFECT_OTHER ) {
993  if ( !special_affects_opponent(special, is_attacker_) )
994  return false;
995  }
996 
997  // Is this active on attack/defense?
998  const std::string & active_on = special["active_on"];
999  if ( !active_on.empty() ) {
1000  if ( is_attacker_ && active_on != "offense" )
1001  return false;
1002  if ( !is_attacker_ && active_on != "defense" )
1003  return false;
1004  }
1005 
1006  // Get the units involved.
1007  assert(display::get_singleton());
1008  const unit_map& units = display::get_singleton()->get_units();
1009 
1010  unit_const_ptr self = self_;
1011  unit_const_ptr other = other_;
1012 
1013  if(self == nullptr) {
1015  if(it.valid()) {
1016  self = it.get_shared_ptr().get();
1017  }
1018  }
1019  if(other == nullptr) {
1021  if(it.valid()) {
1022  other = it.get_shared_ptr().get();
1023  }
1024  }
1025 
1026  // Make sure they're facing each other.
1027  temporary_facing self_facing(self, self_loc_.get_relative_dir(other_loc_));
1028  temporary_facing other_facing(other, other_loc_.get_relative_dir(self_loc_));
1029 
1030  // Translate our context into terms of "attacker" and "defender".
1031  unit_const_ptr & att = is_attacker_ ? self : other;
1032  unit_const_ptr & def = is_attacker_ ? other : self;
1033  const map_location & att_loc = is_attacker_ ? self_loc_ : other_loc_;
1034  const map_location & def_loc = is_attacker_ ? other_loc_ : self_loc_;
1035  const_attack_ptr att_weapon = is_attacker_ ? shared_from_this() : other_attack_;
1036  const_attack_ptr def_weapon = is_attacker_ ? other_attack_ : shared_from_this();
1037 
1038  // Filter the units involved.
1039  if (!special_unit_matches(self, other, self_loc_, shared_from_this(), special, is_for_listing_, "filter_self"))
1040  return false;
1041  if (!special_unit_matches(other, self, other_loc_, other_attack_, special, is_for_listing_, "filter_opponent"))
1042  return false;
1043  if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing_, "filter_attacker"))
1044  return false;
1045  if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing_, "filter_defender"))
1046  return false;
1047 
1048  adjacent_loc_array_t adjacent;
1049  get_adjacent_tiles(self_loc_, adjacent.data());
1050 
1051  // Filter the adjacent units.
1052  for (const config &i : special.child_range("filter_adjacent"))
1053  {
1054  std::size_t count = 0;
1055  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
1056  unit_filter filter{ vconfig(i) };
1057  for (const map_location::DIRECTION index : dirs)
1058  {
1060  continue;
1061  unit_map::const_iterator unit = units.find(adjacent[index]);
1062  if (unit == units.end() || !filter.matches(*unit, adjacent[index], *self))
1063  return false;
1064  if (i.has_attribute("is_enemy")) {
1066  if (i["is_enemy"].to_bool() != dc.get_team(unit->side()).is_enemy(self->side())) {
1067  continue;
1068  }
1069  }
1070  count++;
1071  }
1072  if (i["count"].empty() && count != dirs.size()) {
1073  return false;
1074  }
1075  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
1076  return false;
1077  }
1078  }
1079 
1080  // Filter the adjacent locations.
1081  for (const config &i : special.child_range("filter_adjacent_location"))
1082  {
1083  std::size_t count = 0;
1084  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
1085  terrain_filter adj_filter(vconfig(i), resources::filter_con);
1086  for (const map_location::DIRECTION index : dirs)
1087  {
1089  continue;
1090  if(!adj_filter.match(adjacent[index])) {
1091  return false;
1092  }
1093  count++;
1094  }
1095  if (i["count"].empty() && count != dirs.size()) {
1096  return false;
1097  }
1098  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
1099  return false;
1100  }
1101  }
1102 
1103  return true;
1104 }
1105 
1106 
1107 
1109 {
1110 
1111 void individual_effect::set(value_modifier t, int val, const config *abil, const map_location &l)
1112 {
1113  type=t;
1114  value=val;
1115  ability=abil;
1116  loc=l;
1117 }
1118 
1119 bool filter_base_matches(const config& cfg, int def)
1120 {
1121  if (const config &apply_filter = cfg.child("filter_base_value")) {
1122  config::attribute_value cond_eq = apply_filter["equals"];
1123  config::attribute_value cond_ne = apply_filter["not_equals"];
1124  config::attribute_value cond_lt = apply_filter["less_than"];
1125  config::attribute_value cond_gt = apply_filter["greater_than"];
1126  config::attribute_value cond_ge = apply_filter["greater_than_equal_to"];
1127  config::attribute_value cond_le = apply_filter["less_than_equal_to"];
1128  return (cond_eq.empty() || def == cond_eq.to_int()) &&
1129  (cond_ne.empty() || def != cond_ne.to_int()) &&
1130  (cond_lt.empty() || def < cond_lt.to_int()) &&
1131  (cond_gt.empty() || def > cond_gt.to_int()) &&
1132  (cond_ge.empty() || def >= cond_ge.to_int()) &&
1133  (cond_le.empty() || def <= cond_le.to_int());
1134  }
1135  return true;
1136 }
1137 
1138 effect::effect(const unit_ability_list& list, int def, bool backstab) :
1139  effect_list_(),
1140  composite_value_(0)
1141 {
1142 
1143  int value_set = def;
1144  bool value_is_set = false;
1145  std::map<std::string,individual_effect> values_add;
1146  std::map<std::string,individual_effect> values_mul;
1147  std::map<std::string,individual_effect> values_div;
1148 
1149  individual_effect set_effect;
1150 
1151  for (const unit_ability & ability : list) {
1152  const config& cfg = *ability.first;
1153  const std::string& effect_id = cfg[cfg["id"].empty() ? "name" : "id"];
1154 
1155  if (!cfg["backstab"].blank()) {
1156  deprecated_message("backstab= in weapon specials", DEP_LEVEL::PREEMPTIVE, {1, 15, 0}, "Use [filter_adjacent] instead.");
1157  }
1158 
1159  if (!backstab && cfg["backstab"].to_bool())
1160  continue;
1161  if (!filter_base_matches(cfg, def))
1162  continue;
1163 
1164  if (const config::attribute_value *v = cfg.get("value")) {
1165  int value = get_single_ability_value(*v, def, ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1166  callable.add("base_value", wfl::variant(def));
1167  return formula.evaluate(callable).as_int();
1168  });
1169 
1170  bool cumulative = cfg["cumulative"].to_bool();
1171  if (!value_is_set && !cumulative) {
1172  value_set = value;
1173  set_effect.set(SET, value, ability.first, ability.second);
1174  } else {
1175  if (cumulative) value_set = std::max<int>(value_set, def);
1176  if (value > value_set) {
1177  value_set = value;
1178  set_effect.set(SET, value, ability.first, ability.second);
1179  }
1180  }
1181  value_is_set = true;
1182  }
1183 
1184  if (const config::attribute_value *v = cfg.get("add")) {
1185  int add = get_single_ability_value(*v, def, ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1186  callable.add("base_value", wfl::variant(def));
1187  return formula.evaluate(callable).as_int();
1188  });
1189  std::map<std::string,individual_effect>::iterator add_effect = values_add.find(effect_id);
1190  if(add_effect == values_add.end() || add > add_effect->second.value) {
1191  values_add[effect_id].set(ADD, add, ability.first, ability.second);
1192  }
1193  }
1194  if (const config::attribute_value *v = cfg.get("sub")) {
1195  int sub = - get_single_ability_value(*v, def, ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1196  callable.add("base_value", wfl::variant(def));
1197  return formula.evaluate(callable).as_int();
1198  });
1199  std::map<std::string,individual_effect>::iterator sub_effect = values_add.find(effect_id);
1200  if(sub_effect == values_add.end() || sub > sub_effect->second.value) {
1201  values_add[effect_id].set(ADD, sub, ability.first, ability.second);
1202  }
1203  }
1204  if (const config::attribute_value *v = cfg.get("multiply")) {
1205  int multiply = static_cast<int>(get_single_ability_value(*v, static_cast<double>(def), ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1206  callable.add("base_value", wfl::variant(def));
1207  return formula.evaluate(callable).as_decimal() / 1000.0 ;
1208  }) * 100);
1209  std::map<std::string,individual_effect>::iterator mul_effect = values_mul.find(effect_id);
1210  if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
1211  values_mul[effect_id].set(MUL, multiply, ability.first, ability.second);
1212  }
1213  }
1214  if (const config::attribute_value *v = cfg.get("divide")) {
1215  int divide = static_cast<int>(get_single_ability_value(*v, static_cast<double>(def), ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) {
1216  callable.add("base_value", wfl::variant(def));
1217  return formula.evaluate(callable).as_decimal() / 1000.0 ;
1218  }) * 100);
1219 
1220  if (divide == 0) {
1221  ERR_NG << "division by zero with divide= in ability/weapon special " << effect_id << std::endl;
1222  }
1223  else {
1224  std::map<std::string,individual_effect>::iterator div_effect = values_div.find(effect_id);
1225  if(div_effect == values_div.end() || divide > div_effect->second.value) {
1226  values_div[effect_id].set(DIV, divide, ability.first, ability.second);
1227  }
1228  }
1229  }
1230  }
1231 
1232  if(value_is_set && set_effect.type != NOT_USED) {
1233  effect_list_.push_back(set_effect);
1234  }
1235 
1236  /* Do multiplication with floating point values rather than integers
1237  * We want two places of precision for each multiplier
1238  * Using integers multiplied by 100 to keep precision causes overflow
1239  * after 3-4 abilities for 32-bit values and ~8 for 64-bit
1240  * Avoiding the overflow by dividing after each step introduces rounding errors
1241  * that may vary depending on the order effects are applied
1242  * As the final values are likely <1000 (always true for mainline), loss of less significant digits is not an issue
1243  */
1244  double multiplier = 1.0;
1245  double divisor = 1.0;
1246 
1247  for(const auto& val : values_mul) {
1248  multiplier *= val.second.value/100.0;
1249  effect_list_.push_back(val.second);
1250  }
1251 
1252  for(const auto& val : values_div) {
1253  divisor *= val.second.value/100.0;
1254  effect_list_.push_back(val.second);
1255  }
1256 
1257  int addition = 0;
1258  for(const auto& val : values_add) {
1259  addition += val.second.value;
1260  effect_list_.push_back(val.second);
1261  }
1262 
1263  composite_value_ = static_cast<int>((value_set + addition) * multiplier / divisor);
1264 }
1265 
1266 } // end namespace unit_abilities
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
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:701
bool ability_affects_weapon(const config &cfg, const_attack_ptr weapon, bool is_opp) const
Definition: abilities.cpp:417
std::vector< individual_effect > effect_list_
Definition: abilities.hpp:56
bool empty() const
Definition: unit.hpp:78
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:423
unit_iterator end()
Definition: map.hpp:415
#define ERR_NG
Definition: abilities.cpp:43
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:88
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:923
const team & get_team(int side) const
virtual const display_context & get_disp_context() const =0
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:517
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:173
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:225
This class represents a single unit of a specific type.
Definition: unit.hpp:99
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:39
unit_filter & set_use_flat_tod(bool value)
Definition: filter.hpp:121
void emplace_back(T &&... args)
Definition: unit.hpp:87
Variant for storing WML attributes.
std::string filename
Definition: formula.hpp:107
New lexcical_cast header.
int as_int() const
Definition: variant.cpp:298
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:416
void set(value_modifier t, int val, const config *abil, const map_location &l)
Definition: abilities.cpp:1111
child_itors child_range(config_key_type key)
Definition: config.cpp:366
map_location other_loc_
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:820
const unit_map & get_units() const
Definition: display.hpp:121
bool has_ability_type(const std::string &ability) const
Check if the unit has an ability of a specific type.
Definition: abilities.cpp:430
unit_const_ptr other_
const std::string & type() const
Definition: attack_type.hpp:42
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
Definition: filter.hpp:129
#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:123
-file sdl_utils.hpp
std::pair< int, bool > combat_ability(const std::string &ability, int abil_value=0, bool backstab_pos=false) const
Definition: attack.cpp:1711
int num_attacks() const
Definition: attack_type.hpp:51
A single unit type that the player may recruit.
Definition: types.hpp:42
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:1119
map_location loc_
static const char * name(const std::vector< SDL_Joystick *> &joysticks, const std::size_t index)
Definition: joystick.cpp:48
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
int as_decimal() const
Returns variant&#39;s internal representation of decimal number: ie, 1.234 is represented as 1234...
Definition: variant.cpp:307
bool get_special_bool(const std::string &special, bool simple_check=false) const
Returns whether or not *this has a special with a tag or id equal to special.
Definition: abilities.cpp:598
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:29
std::pair< const config *, map_location > unit_ability
Definition: unit.hpp:48
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:252
bool ability_affects_adjacent(const std::string &ability, const config &cfg, int dir, const map_location &loc, const unit &from) const
Check if an ability affects adjacent units.
Definition: abilities.cpp:385
filter_context * filter_con
Definition: resources.cpp:23
bool valid() const
Definition: location.hpp:93
specials_context_t(const attack_type &weapon, bool attacking)
Initialize weapon specials context for listing.
Definition: abilities.cpp:788
std::string type
Definition: formula.hpp:105
bool blank() const
Tests for an attribute that was never set.
bool is_enemy(int n) const
Definition: team.hpp:243
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:665
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:497
std::array< map_location, 6 > adjacent_loc_array_t
Definition: location.hpp:170
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:633
Encapsulates the map of the game.
Definition: location.hpp:42
unit_iterator find(std::size_t id)
Definition: map.cpp:311
#define UNUSED(x)
Definition: global.hpp:34
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:141
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:220
std::size_t i
Definition: function.cpp:933
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:269
bool is_for_listing_
int damage() const
Definition: attack_type.hpp:50
std::vector< std::tuple< std::string, t_string, t_string, t_string > > ability_tooltips(boost::dynamic_bitset<> *active_list=nullptr) const
Gets the names and descriptions of this unit&#39;s abilities.
Definition: abilities.cpp:268
mock_party p
static map_location::DIRECTION s
unit_const_ptr self_
int modified_damage(bool is_backstab) const
Returns the damage per attack of this weapon, considering specials.
Definition: abilities.cpp:850
const display_context & get_disp_context() const
Definition: display.hpp:168
int get_composite_value() const
Definition: abilities.hpp:49
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:44
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr) const
Gets the unit&#39;s active abilities of a particular type if it were on a specified location.
Definition: abilities.cpp:181
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
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:409
bool empty() const
Definition: tstring.hpp:182
double t
Definition: astarsearch.cpp:64
bool find(E event, F functor)
Tests whether an event handler is available.
const_attack_ptr other_attack_
A variable-expanding proxy for the config class.
Definition: variable.hpp:42
Standard logging facilities (interface).
V::result_type apply_visitor(const V &visitor) const
Applies a visitor to the underlying variant.
static const map_location & null_location()
Definition: location.hpp:85
Container associating units to locations.
Definition: map.hpp:99
#define e
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
Definition: abilities.cpp:226
bool special_active(const config &special, AFFECTS whom, bool include_backstab=true) const
Returns whether or not the given special is active for the specified unit, based on the current conte...
Definition: abilities.cpp:977
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:313
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
bool valid() const
Definition: map.hpp:276
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:37
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
bool empty() const
Definition: config.cpp:886
map_location self_loc_
static lg::log_domain log_engine("engine")
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: unit.hpp:56