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