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