The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
abilities.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2016 by Dominic Bolin <[email protected]>
3  Part of the Battle for Wesnoth Project http://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 "game_board.hpp"
21 #include "log.hpp"
22 #include "resources.hpp"
23 #include "team.hpp"
24 #include "terrain/filter.hpp"
25 #include "units/unit.hpp"
26 #include "units/abilities.hpp"
27 #include "units/filter.hpp"
28 #include "units/map.hpp"
29 #include "filter_context.hpp"
30 
31 static lg::log_domain log_engine("engine");
32 #define ERR_NG LOG_STREAM(err, log_engine)
33 
34 namespace {
35  class temporary_facing
36  {
37  map_location::DIRECTION save_dir_;
39  public:
40  temporary_facing(unit_map::const_iterator u, map_location::DIRECTION new_dir)
41  : save_dir_(u.valid() ? u->facing() : map_location::NDIRECTIONS)
42  , u_(u)
43  {
44  if (u_.valid()) {
45  u_->set_facing(new_dir);
46  }
47  }
48  ~temporary_facing()
49  {
50  if (u_.valid()) {
51  u_->set_facing(save_dir_);
52  }
53  }
54  };
55 }
56 
57 /*
58  *
59  * [abilities]
60  * ...
61  *
62  * [heals]
63  * value=4
64  * max_value=8
65  * cumulative=no
66  * affect_allies=yes
67  * name= _ "heals"
68  * female_name= _ "female^heals"
69  * name_inactive=null
70  * female_name_inactive=null
71  * description= _ "Heals:
72 Allows the unit to heal adjacent friendly units at the beginning of each turn.
73 
74 A unit cared for by a healer may heal up to 4 HP per turn.
75 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."
76  * description_inactive=null
77  *
78  * affect_self=yes
79  * [filter] // SUF
80  * ...
81  * [/filter]
82  * [filter_self] // SUF
83  * ...
84  * [/filter_self]
85  * [filter_adjacent] // SUF
86  * adjacent=n,ne,nw
87  * ...
88  * [/filter_adjacent]
89  * [filter_adjacent_location]
90  * adjacent=n,ne,nw
91  * ...
92  * [/filter_adjacent]
93  * [affect_adjacent]
94  * adjacent=n,ne,nw
95  * [filter] // SUF
96  * ...
97  * [/filter]
98  * [/affect_adjacent]
99  * [affect_adjacent]
100  * adjacent=s,se,sw
101  * [filter] // SUF
102  * ...
103  * [/filter]
104  * [/affect_adjacent]
105  *
106  * [/heals]
107  *
108  * ...
109  * [/abilities]
110  *
111  */
112 
113 
114 namespace {
115 
116 bool affects_side(const config& cfg, const std::vector<team>& teams, size_t side, size_t other_side)
117 {
118  if (side == other_side)
119  return cfg["affect_allies"].to_bool(true);
120  if (teams[side - 1].is_enemy(other_side))
121  return cfg["affect_enemies"].to_bool();
122  else
123  return cfg["affect_allies"].to_bool();
124 }
125 
126 }
127 
128 
129 bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc) const
130 {
131  assert(resources::teams);
132 
133  for (const config &i : this->abilities_.child_range(tag_name)) {
134  if (ability_active(tag_name, i, loc) &&
135  ability_affects_self(tag_name, i, loc))
136  {
137  return true;
138  }
139  }
140 
141  const unit_map& units = *resources::units;
142  map_location adjacent[6];
143  get_adjacent_tiles(loc,adjacent);
144  for(int i = 0; i != 6; ++i) {
145  const unit_map::const_iterator it = units.find(adjacent[i]);
146  if (it == units.end() || it->incapacitated())
147  continue;
148  // Abilities may be tested at locations other than the unit's current
149  // location. This is intentional to allow for less messing with the unit
150  // map during calculations, particularly with regards to movement.
151  // Thus, we need to make sure the adjacent unit (*it) is not actually
152  // ourself.
153  if ( &*it == this )
154  continue;
155  for (const config &j : it->abilities_.child_range(tag_name)) {
156  if (affects_side(j, *resources::teams, side(), it->side()) &&
157  it->ability_active(tag_name, j, adjacent[i]) &&
158  ability_affects_adjacent(tag_name, j, i, loc, *it))
159  {
160  return true;
161  }
162  }
163  }
164 
165 
166  return false;
167 }
169 {
170  assert(resources::teams);
171 
173 
174  for (const config &i : this->abilities_.child_range(tag_name)) {
175  if (ability_active(tag_name, i, loc) &&
176  ability_affects_self(tag_name, i, loc))
177  {
178  res.push_back(unit_ability(&i, loc));
179  }
180  }
181 
182  const unit_map& units = *resources::units;
183  map_location adjacent[6];
184  get_adjacent_tiles(loc,adjacent);
185  for(int i = 0; i != 6; ++i) {
186  const unit_map::const_iterator it = units.find(adjacent[i]);
187  if (it == units.end() || it->incapacitated())
188  continue;
189  // Abilities may be tested at locations other than the unit's current
190  // location. This is intentional to allow for less messing with the unit
191  // map during calculations, particularly with regards to movement.
192  // Thus, we need to make sure the adjacent unit (*it) is not actually
193  // ourself.
194  if ( &*it == this )
195  continue;
196  for (const config &j : it->abilities_.child_range(tag_name)) {
197  if (affects_side(j, *resources::teams, side(), it->side()) &&
198  it->ability_active(tag_name, j, adjacent[i]) &&
199  ability_affects_adjacent(tag_name, j, i, loc, *it))
200  {
201  res.push_back(unit_ability(&j, adjacent[i]));
202  }
203  }
204  }
205 
206 
207  return res;
208 }
209 
210 std::vector<std::string> unit::get_ability_list() const
211 {
212  std::vector<std::string> res;
213 
214  for (const config::any_child &ab : this->abilities_.all_children_range()) {
215  std::string const &id = ab.cfg["id"];
216  if (!id.empty())
217  res.push_back(id);
218  }
219  return res;
220 }
221 
222 
223 namespace {
224  // These functions might have wider usefulness than this file, but for now
225  // I'll make them local.
226 
227  /**
228  * Chooses a value from the given config. If the value specified by @a key is
229  * blank, then @a default_key is chosen instead.
230  */
231  inline const config::attribute_value & default_value(
232  const config & cfg, const std::string & key, const std::string & default_key)
233  {
234  const config::attribute_value & value = cfg[key];
235  return !value.blank() ? value : cfg[default_key];
236  }
237 
238  /**
239  * Chooses a value from the given config based on gender. If the value for
240  * the specified gender is blank, then @a default_key is chosen instead.
241  */
242  inline const config::attribute_value & gender_value(
243  const config & cfg, unit_race::GENDER gender, const std::string & male_key,
244  const std::string & female_key, const std::string & default_key)
245  {
246  return default_value(cfg,
247  gender == unit_race::MALE ? male_key : female_key,
248  default_key);
249  }
250 }
251 
252 /**
253  * Returns names and descriptions of the unit's abilities.
254  * The returned triples consist of (in order) base name, male or female name as
255  * appropriate for the unit, and description.
256  * @param active_list If nullptr, then all abilities are forced active. If not
257  * null, this vector will be the same length as the returned
258  * one and will indicate whether or not the corresponding
259  * ability is active.
260  */
261 std::vector<boost::tuple<t_string,t_string,t_string> > unit::ability_tooltips(std::vector<bool> *active_list) const
262 {
263  std::vector<boost::tuple<t_string,t_string,t_string> > res;
264  if ( active_list )
265  active_list->clear();
266 
267  for (const config::any_child &ab : this->abilities_.all_children_range())
268  {
269  if ( !active_list || ability_active(ab.key, ab.cfg, loc_) )
270  {
271  t_string const &name =
272  gender_value(ab.cfg, gender_, "name", "female_name", "name").t_str();
273 
274  if (!name.empty()) {
275  res.push_back(boost::make_tuple(
276  ab.cfg["name"].t_str(),
277  name,
278  ab.cfg["description"].t_str() ));
279  if ( active_list )
280  active_list->push_back(true);
281  }
282  }
283  else
284  {
285  // See if an inactive name was specified.
286  config::attribute_value const &inactive_value =
287  gender_value(ab.cfg, gender_, "name_inactive",
288  "female_name_inactive", "name_inactive");
289  t_string const &name = !inactive_value.blank() ? inactive_value.t_str() :
290  gender_value(ab.cfg, gender_, "name", "female_name", "name").t_str();
291 
292  if (!name.empty()) {
293  res.push_back(boost::make_tuple(
294  default_value(ab.cfg, "name_inactive", "name").t_str(),
295  name,
296  default_value(ab.cfg, "description_inactive", "description").t_str() ));
297  active_list->push_back(false);
298  }
299  }
300  }
301  return res;
302 }
303 
304 /*
305  *
306  * cfg: an ability WML structure
307  *
308  */
309 bool unit::ability_active(const std::string& ability,const config& cfg,const map_location& loc) const
310 {
311  bool illuminates = ability == "illuminates";
313 
314  if (const config &afilter = cfg.child("filter"))
315  if ( !unit_filter(vconfig(afilter), resources::filter_con, illuminates).matches(*this, loc) )
316  return false;
317 
318  map_location adjacent[6];
319  get_adjacent_tiles(loc,adjacent);
320  const unit_map& units = *resources::units;
321 
322  for (const config &i : cfg.child_range("filter_adjacent"))
323  {
324  size_t count = 0;
325  const unit_filter ufilt(vconfig(i), resources::filter_con, illuminates);
326  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
327  for (const map_location::DIRECTION index : dirs)
328  {
330  continue;
331  unit_map::const_iterator unit = units.find(adjacent[index]);
332  if (unit == units.end())
333  return false;
334  if (!ufilt(*unit, *this))
335  return false;
336  if (i.has_attribute("is_enemy")) {
338  if (i["is_enemy"].to_bool() != dc.teams()[unit->side() - 1].is_enemy(side_)) {
339  continue;
340  }
341  }
342  count++;
343  }
344  if (i["count"].empty() && count != dirs.size()) {
345  return false;
346  }
347  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
348  return false;
349  }
350  }
351 
352  for (const config &i : cfg.child_range("filter_adjacent_location"))
353  {
354  size_t count = 0;
356  adj_filter.flatten(illuminates);
357 
358  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
359  for (const map_location::DIRECTION index : dirs)
360  {
362  continue;
363  }
364  if(!adj_filter.match(adjacent[index])) {
365  return false;
366  }
367  count++;
368  }
369  if (i["count"].empty() && count != dirs.size()) {
370  return false;
371  }
372  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
373  return false;
374  }
375  }
376  return true;
377 }
378 /*
379  *
380  * cfg: an ability WML structure
381  *
382  */
383 bool unit::ability_affects_adjacent(const std::string& ability, const config& cfg,int dir,const map_location& loc,const unit& from) const
384 {
385  bool illuminates = ability == "illuminates";
386 
387  assert(dir >=0 && dir <= 5);
388  map_location::DIRECTION direction = static_cast<map_location::DIRECTION>(dir);
389 
390  for (const config &i : cfg.child_range("affect_adjacent"))
391  {
392  if (i.has_attribute("adjacent")) { //key adjacent defined
393  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
394  if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
395  continue;
396  }
397  }
398  const config &filter = i.child("filter");
399  if (!filter || //filter tag given
400  unit_filter(vconfig(filter), resources::filter_con, illuminates).matches(*this, loc, from) ) {
401  return true;
402  }
403  }
404  return false;
405 }
406 /*
407  *
408  * cfg: an ability WML structure
409  *
410  */
411 bool unit::ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const
412 {
413  const config &filter = cfg.child("filter_self");
414  bool affect_self = cfg["affect_self"].to_bool(true);
415  if (!filter || !affect_self) return affect_self;
416  return unit_filter(vconfig(filter), resources::filter_con, ability == "illuminates").matches(*this, loc);
417 }
418 
419 bool unit::has_ability_type(const std::string& ability) const
420 {
421  config::const_child_itors itors = this->abilities_.child_range(ability);
422  return itors.first != itors.second;
423 }
424 
425 
426 std::pair<int,map_location> unit_ability_list::highest(const std::string& key, int def) const
427 {
428  if ( cfgs_.empty() ) {
429  return std::make_pair(def, map_location());
430  }
431  // The returned location is the best non-cumulative one, if any,
432  // the best absolute cumulative one otherwise.
433  map_location best_loc;
434  bool only_cumulative = true;
435  int abs_max = 0;
436  int flat = 0;
437  int stack = 0;
438  for (unit_ability const &p : cfgs_)
439  {
440  int value = (*p.first)[key].to_int(def);
441  if ((*p.first)["cumulative"].to_bool()) {
442  stack += value;
443  if (value < 0) value = -value;
444  if (only_cumulative && value >= abs_max) {
445  abs_max = value;
446  best_loc = p.second;
447  }
448  } else if (only_cumulative || value > flat) {
449  only_cumulative = false;
450  flat = value;
451  best_loc = p.second;
452  }
453  }
454  return std::make_pair(flat + stack, best_loc);
455 }
456 
457 std::pair<int,map_location> unit_ability_list::lowest(const std::string& key, int def) const
458 {
459  if ( cfgs_.empty() ) {
460  return std::make_pair(def, map_location());
461  }
462  // The returned location is the best non-cumulative one, if any,
463  // the best absolute cumulative one otherwise.
464  map_location best_loc;
465  bool only_cumulative = true;
466  int abs_max = 0;
467  int flat = 0;
468  int stack = 0;
469  for (unit_ability const &p : cfgs_)
470  {
471  int value = (*p.first)[key].to_int(def);
472  if ((*p.first)["cumulative"].to_bool()) {
473  stack += value;
474  if (value < 0) value = -value;
475  if (only_cumulative && value <= abs_max) {
476  abs_max = value;
477  best_loc = p.second;
478  }
479  } else if (only_cumulative || value < flat) {
480  only_cumulative = false;
481  flat = value;
482  best_loc = p.second;
483  }
484  }
485  return std::make_pair(flat + stack, best_loc);
486 }
487 
488 /*
489  *
490  * [special]
491  * [swarm]
492  * name= _ "swarm"
493  * name_inactive= _ ""
494  * description= _ ""
495  * description_inactive= _ ""
496  * cumulative=no
497  * apply_to=self #self,opponent,defender,attacker,both
498  * #active_on=defense # or offense; omitting this means "both"
499  *
500  * swarm_attacks_max=4
501  * swarm_attacks_min=2
502  *
503  * [filter_self] // SUF
504  * ...
505  * [/filter_self]
506  * [filter_opponent] // SUF
507  * [filter_attacker] // SUF
508  * [filter_defender] // SUF
509  * [filter_adjacent] // SAUF
510  * [filter_adjacent_location] // SAUF + locs
511  * [/swarm]
512  * [/special]
513  *
514  */
515 
516 namespace {
517 
518  /**
519  * Gets the children of @parent (which should be the specials for an
520  * attack_type) and places the ones whose tag or id= matches @a id into
521  * @a result.
522  * If @a just_peeking is set to true, then @a result is not touched;
523  * instead the return value is used to indicate if any matching children
524  * were found.
525  *
526  * @returns true if @a just_peeking is true and a match was found;
527  * false otherwise.
528  */
529  bool get_special_children(std::vector<const config*>& result, const config& parent,
530  const std::string& id, bool just_peeking=false) {
531  for (const config::any_child &sp : parent.all_children_range())
532  {
533  if (sp.key == id || sp.cfg["id"] == id) {
534  if(just_peeking) {
535  return true; // peek succeeded; done
536  } else {
537  result.push_back(&sp.cfg);
538  }
539  }
540  }
541  return false;
542  }
543 }
544 
545 /**
546  * Returns whether or not @a *this has a special with a tag or id equal to
547  * @a special. If @a simple_check is set to true, then the check is merely
548  * for being present. Otherwise (the default), the check is for a special
549  * active in the current context (see set_specials_context), including
550  * specials obtained from the opponent's attack.
551  */
552 bool attack_type::get_special_bool(const std::string& special, bool simple_check) const
553 {
554  {
555  std::vector<const config*> list;
556  if ( get_special_children(list, specials_, special, simple_check) ) {
557  return true;
558  }
559  // If we make it to here, then either list.empty() or !simple_check.
560  // So if the list is not empty, then this is not a simple check and
561  // we need to check each special in the list to see if any are active.
562  for (std::vector<const config*>::iterator i = list.begin(), i_end = list.end(); i != i_end; ++i) {
563  if ( special_active(**i, AFFECT_SELF) ) {
564  return true;
565  }
566  }
567  }
568  // Skip checking the opponent's attack?
569  if ( simple_check || !other_attack_ ) {
570  return false;
571  }
572 
573  std::vector<const config*> list;
574  get_special_children(list, other_attack_->specials_, special);
575  for (std::vector<const config*>::iterator i = list.begin(), i_end = list.end(); i != i_end; ++i) {
577  return true;
578  }
579  }
580  return false;
581 }
582 
583 /**
584  * Returns the currently active specials as an ability list, given the current
585  * context (see set_specials_context).
586  */
588 {
589  //log_scope("get_specials");
591  for (const config &i : specials_.child_range(special)) {
592  if ( special_active(i, AFFECT_SELF) )
594  }
595  if (!other_attack_) return res;
596  for (const config &i : other_attack_->specials_.child_range(special)) {
599  }
600  return res;
601 }
602 
603 /**
604  * Returns a vector of names and descriptions for the specials of *this.
605  * Each std::pair in the vector has first = name and second = description.
606  *
607  * This uses either the active or inactive name/description for each special,
608  * based on the current context (see set_specials_context), provided
609  * @a active_list is not nullptr. Otherwise specials are assumed active.
610  * If the appropriate name is empty, the special is skipped.
611  */
612 std::vector<std::pair<t_string, t_string> > attack_type::special_tooltips(
613  std::vector<bool> *active_list) const
614 {
615  //log_scope("special_tooltips");
616  std::vector<std::pair<t_string, t_string> > res;
617  if ( active_list )
618  active_list->clear();
619 
621  {
622  if ( !active_list || special_active(sp.cfg, AFFECT_EITHER) ) {
623  const t_string &name = sp.cfg["name"];
624  if (!name.empty()) {
625  res.push_back(std::make_pair(name, sp.cfg["description"].t_str() ));
626  if ( active_list )
627  active_list->push_back(true);
628  }
629  } else {
630  t_string const &name = default_value(sp.cfg, "name_inactive", "name").t_str();
631  if (!name.empty()) {
632  res.push_back(std::make_pair(
633  name, default_value(sp.cfg, "description_inactive", "description").t_str() ));
634  active_list->push_back(false);
635  }
636  }
637  }
638  return res;
639 }
640 
641 /**
642  * Returns a comma-separated string of active names for the specials of *this.
643  * Empty names are skipped.
644  *
645  * This excludes inactive specials if only_active is true. Whether or not a
646  * special is active depends on the current context (see set_specials_context)
647  * and the @a is_backstab parameter.
648  */
649 std::string attack_type::weapon_specials(bool only_active, bool is_backstab) const
650 {
651  //log_scope("weapon_specials");
654  {
655  if ( only_active && !special_active(sp.cfg, AFFECT_EITHER, is_backstab) )
656  continue;
657 
658  std::string const &name = sp.cfg["name"].str();
659  if (!name.empty()) {
660  if (!res.empty()) res += ',';
661  res += name;
662  }
663  }
664 
665  return res;
666 }
667 
668 
669 /**
670  * Sets the context under which specials will be checked for being active.
671  * This version is appropriate if both units in a combat are known.
672  * @param[in] unit_loc The location of the unit with this weapon.
673  * @param[in] other_loc The location of the other unit in the combat.
674  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
675  * @param[in] other_attack The attack used by the other unit.
676  */
678  const map_location& other_loc,
679  bool attacking,
680  const attack_type *other_attack) const
681 {
682  self_loc_ = unit_loc;
683  other_loc_ = other_loc;
684  is_attacker_ = attacking;
685  other_attack_ = other_attack;
686  is_for_listing_ = false;
687 }
688 
689 /**
690  * Sets the context under which specials will be checked for being active.
691  * This version is appropriate if there is no specific combat being considered.
692  * @param[in] loc The location of the unit with this weapon.
693  * @param[in] attacking Whether or not the unit with this weapon is the attacker.
694  */
695 void attack_type::set_specials_context(const map_location& loc, bool attacking) const
696 {
697  self_loc_ = loc;
699  is_attacker_ = attacking;
700  other_attack_ = nullptr;
701  is_for_listing_ = false;
702 }
703 
705 {
706  is_for_listing_ = true;
707 }
708 
709 
710 /**
711  * Calculates the number of attacks this weapon has, considering specials.
712  * This returns two numbers because of the swarm special. The actual number of
713  * attacks depends on the unit's health and should be:
714  * min_attacks + (max_attacks - min_attacks) * (current hp) / (max hp)
715  * c.f. swarm_blows()
716  */
717 void attack_type::modified_attacks(bool is_backstab, unsigned & min_attacks,
718  unsigned & max_attacks) const
719 {
720  // Apply [attacks].
721  unit_abilities::effect attacks_effect(get_specials("attacks"),
722  num_attacks(), is_backstab);
723  int attacks_value = attacks_effect.get_composite_value();
724  if ( attacks_value < 0 ) {
725  attacks_value = num_attacks();
726  ERR_NG << "negative number of strikes after applying weapon specials" << std::endl;
727  }
728 
729  // Apply [swarm].
730  unit_ability_list swarm_specials = get_specials("swarm");
731  if ( !swarm_specials.empty() ) {
732  min_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_min").first);
733  max_attacks = std::max<int>(0, swarm_specials.highest("swarm_attacks_max", attacks_value).first);
734  } else {
735  min_attacks = max_attacks = attacks_value;
736  }
737 }
738 
739 
740 /**
741  * Returns the damage per attack of this weapon, considering specials.
742  */
743 int attack_type::modified_damage(bool is_backstab) const
744 {
745  unit_abilities::effect dmg_effect(get_specials("damage"), damage(), is_backstab);
746  return dmg_effect.get_composite_value();
747 }
748 
749 
750 namespace { // Helpers for attack_type::special_active()
751 
752  /**
753  * Returns whether or not the given special affects the opponent of the unit
754  * with the special.
755  * @param[in] special a weapon special WML structure
756  * @param[in] is_attacker whether or not the unit with the special is the attacker
757  */
758  bool special_affects_opponent(const config& special, bool is_attacker)
759  {
760  //log_scope("special_affects_opponent");
761  std::string const &apply_to = special["apply_to"];
762  if ( apply_to.empty() )
763  return false;
764  if ( apply_to == "both" )
765  return true;
766  if ( apply_to == "opponent" )
767  return true;
768  if ( is_attacker && apply_to == "defender" )
769  return true;
770  if ( !is_attacker && apply_to == "attacker" )
771  return true;
772  return false;
773  }
774 
775  /**
776  * Returns whether or not the given special affects the unit with the special.
777  * @param[in] special a weapon special WML structure
778  * @param[in] is_attacker whether or not the unit with the special is the attacker
779  */
780  bool special_affects_self(const config& special, bool is_attacker)
781  {
782  //log_scope("special_affects_self");
783  std::string const &apply_to = special["apply_to"];
784  if ( apply_to.empty() )
785  return true;
786  if ( apply_to == "both" )
787  return true;
788  if ( apply_to == "self" )
789  return true;
790  if ( is_attacker && apply_to == "attacker" )
791  return true;
792  if ( !is_attacker && apply_to == "defender")
793  return true;
794  return false;
795  }
796 
797  /**
798  * Determines if a unit/weapon combination matches the specified child
799  * (normally a [filter_*] child) of the provided filter.
800  * @param[in] un_it The unit to filter.
801  * @param[in] loc The presumed location of @a un_it.
802  * @param[in] weapon The attack_type to filter.
803  * @param[in] filter The filter containing the child filter to use.
804  * @param[in] child_tag The tag of the child filter to use.
805  */
806  static bool special_unit_matches(const unit_map::const_iterator & un_it,
808  const map_location & loc,
809  const attack_type * weapon,
810  const config & filter,
811  const bool for_listing,
812  const std::string & child_tag)
813  {
814  if (for_listing && !loc.valid())
815  // The special's context was set to ignore this unit, so assume we pass.
816  // (This is used by reports.cpp to show active specials when the
817  // opponent is not known. From a player's perspective, the special
818  // is active, in that it can be used, even though the player might
819  // need to select an appropriate opponent.)
820  return true;
821 
822  const config & filter_child = filter.child(child_tag);
823  if ( !filter_child )
824  // The special does not filter on this unit, so we pass.
825  return true;
826 
827  // If the primary unit doesn't exist, there's nothing to match
828  if (!un_it.valid()) {
829  return false;
830  }
831 
832  unit_filter ufilt(vconfig(filter_child), resources::filter_con);
833 
834  // If the other unit doesn't exist, try matching without it
835  if (!u2.valid()) {
836  return ufilt.matches(*un_it, loc);
837  }
838 
839  // Check for a unit match.
840  if (!ufilt.matches(*un_it, loc, *u2)) {
841  return false;
842  }
843 
844  // Check for a weapon match.
845  if ( const config & filter_weapon = filter_child.child("filter_weapon") ) {
846  if ( !weapon || !weapon->matches_filter(filter_weapon) )
847  return false;
848  }
849 
850  // Passed.
851  return true;
852  }
853 
854 }//anonymous namespace
855 
856 /**
857  * Returns whether or not the given special is active for the specified unit,
858  * based on the current context (see set_specials_context).
859  * @param[in] special a weapon special WML structure
860  * @param[in] whom specifies which combatant we care about
861  * @param[in] include_backstab false if backstab specials should not be active
862  * (usually true since backstab is usually accounted
863  * for elsewhere)
864  */
865 bool attack_type::special_active(const config& special, AFFECTS whom,
866  bool include_backstab) const
867 {
868  //log_scope("special_active");
869 
870  // Backstab check
871  if ( !include_backstab )
872  if ( special["backstab"].to_bool() )
873  return false;
874 
875  // Does this affect the specified unit?
876  if ( whom == AFFECT_SELF ) {
877  if ( !special_affects_self(special, is_attacker_) )
878  return false;
879  }
880  if ( whom == AFFECT_OTHER ) {
881  if ( !special_affects_opponent(special, is_attacker_) )
882  return false;
883  }
884 
885  // Is this active on attack/defense?
886  const std::string & active_on = special["active_on"];
887  if ( !active_on.empty() ) {
888  if ( is_attacker_ && active_on != "offense" )
889  return false;
890  if ( !is_attacker_ && active_on != "defense" )
891  return false;
892  }
893 
894  // Get the units involved.
895  const unit_map & units = *resources::units;
898 
899  // Make sure they're facing each other.
900  temporary_facing self_facing(self, self_loc_.get_relative_dir(other_loc_));
901  temporary_facing other_facing(other, other_loc_.get_relative_dir(self_loc_));
902 
903  // Translate our context into terms of "attacker" and "defender".
904  unit_map::const_iterator & att = is_attacker_ ? self : other;
905  unit_map::const_iterator & def = is_attacker_ ? other : self;
906  const map_location & att_loc = is_attacker_ ? self_loc_ : other_loc_;
907  const map_location & def_loc = is_attacker_ ? other_loc_ : self_loc_;
908  const attack_type * att_weapon = is_attacker_ ? this : other_attack_;
909  const attack_type * def_weapon = is_attacker_ ? other_attack_ : this;
910 
911  // Filter the units involved.
912  if (!special_unit_matches(self, other, self_loc_, this, special, is_for_listing_, "filter_self"))
913  return false;
914  if (!special_unit_matches(other, self, other_loc_, other_attack_, special, is_for_listing_, "filter_opponent"))
915  return false;
916  if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing_, "filter_attacker"))
917  return false;
918  if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing_, "filter_defender"))
919  return false;
920 
921  map_location adjacent[6];
922  get_adjacent_tiles(self_loc_, adjacent);
923 
924  // Filter the adjacent units.
925  for (const config &i : special.child_range("filter_adjacent"))
926  {
927  size_t count = 0;
928  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
930  for (const map_location::DIRECTION index : dirs)
931  {
933  continue;
934  unit_map::const_iterator unit = units.find(adjacent[index]);
935  if (unit == units.end() || !filter.matches(*unit, adjacent[index], *self))
936  return false;
937  if (i.has_attribute("is_enemy")) {
939  if (i["is_enemy"].to_bool() != dc.teams()[unit->side() - 1].is_enemy(self->side())) {
940  continue;
941  }
942  }
943  count++;
944  }
945  if (i["count"].empty() && count != dirs.size()) {
946  return false;
947  }
948  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
949  return false;
950  }
951  }
952 
953  // Filter the adjacent locations.
954  for (const config &i : special.child_range("filter_adjacent_location"))
955  {
956  size_t count = 0;
957  std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
959  for (const map_location::DIRECTION index : dirs)
960  {
962  continue;
963  if(!adj_filter.match(adjacent[index])) {
964  return false;
965  }
966  count++;
967  }
968  if (i["count"].empty() && count != dirs.size()) {
969  return false;
970  }
971  if (!in_ranges<int>(count, utils::parse_ranges(i["count"].str()))) {
972  return false;
973  }
974  }
975 
976  return true;
977 }
978 
979 
980 
981 namespace unit_abilities
982 {
983 
985 {
986  type=t;
987  value=val;
988  ability=abil;
989  loc=l;
990 }
991 
992 bool filter_base_matches(const config& cfg, int def)
993 {
994  if (const config &apply_filter = cfg.child("filter_base_value")) {
995  config::attribute_value cond_eq = apply_filter["equals"];
996  config::attribute_value cond_ne = apply_filter["not_equals"];
997  config::attribute_value cond_lt = apply_filter["less_than"];
998  config::attribute_value cond_gt = apply_filter["greater_than"];
999  config::attribute_value cond_ge = apply_filter["greater_than_equal_to"];
1000  config::attribute_value cond_le = apply_filter["less_than_equal_to"];
1001  return (cond_eq.empty() || def == cond_eq.to_int()) &&
1002  (cond_ne.empty() || def != cond_ne.to_int()) &&
1003  (cond_lt.empty() || def < cond_lt.to_int()) &&
1004  (cond_gt.empty() || def > cond_gt.to_int()) &&
1005  (cond_ge.empty() || def >= cond_ge.to_int()) &&
1006  (cond_le.empty() || def <= cond_le.to_int());
1007  }
1008  return true;
1009 }
1010 
1011 effect::effect(const unit_ability_list& list, int def, bool backstab) :
1012  effect_list_(),
1013  composite_value_(0)
1014 {
1015 
1016  int value_set = def;
1017  bool value_is_set = false;
1018  std::map<std::string,individual_effect> values_add;
1019  std::map<std::string,individual_effect> values_mul;
1020  std::map<std::string,individual_effect> values_div;
1021 
1022  individual_effect set_effect;
1023 
1024  for (const unit_ability & ability : list) {
1025  const config& cfg = *ability.first;
1026  std::string const &effect_id = cfg[cfg["id"].empty() ? "name" : "id"];
1027 
1028  if (!cfg["backstab"].blank()) {
1029  lg::wml_error() << "The backstab= key in weapon specials is deprecated; use [filter_adjacent] instead\n";
1030  }
1031 
1032  if (!backstab && cfg["backstab"].to_bool())
1033  continue;
1034  if (!filter_base_matches(cfg, def))
1035  continue;
1036 
1037  if (const config::attribute_value *v = cfg.get("value")) {
1038  int value = *v;
1039  bool cumulative = cfg["cumulative"].to_bool();
1040  if (!value_is_set && !cumulative) {
1041  value_set = value;
1042  set_effect.set(SET, value, ability.first, ability.second);
1043  } else {
1044  if (cumulative) value_set = std::max<int>(value_set, def);
1045  if (value > value_set) {
1046  value_set = value;
1047  set_effect.set(SET, value, ability.first, ability.second);
1048  }
1049  }
1050  value_is_set = true;
1051  }
1052 
1053  if (const config::attribute_value *v = cfg.get("add")) {
1054  int add = *v;
1055  std::map<std::string,individual_effect>::iterator add_effect = values_add.find(effect_id);
1056  if(add_effect == values_add.end() || add > add_effect->second.value) {
1057  values_add[effect_id].set(ADD, add, ability.first, ability.second);
1058  }
1059  }
1060  if (const config::attribute_value *v = cfg.get("sub")) {
1061  int sub = - *v;
1062  std::map<std::string,individual_effect>::iterator sub_effect = values_add.find(effect_id);
1063  if(sub_effect == values_add.end() || sub > sub_effect->second.value) {
1064  values_add[effect_id].set(ADD, sub, ability.first, ability.second);
1065  }
1066  }
1067  if (const config::attribute_value *v = cfg.get("multiply")) {
1068  int multiply = int(v->to_double() * 100);
1069  std::map<std::string,individual_effect>::iterator mul_effect = values_mul.find(effect_id);
1070  if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) {
1071  values_mul[effect_id].set(MUL, multiply, ability.first, ability.second);
1072  }
1073  }
1074  if (const config::attribute_value *v = cfg.get("divide")) {
1075  if (*v == 0) {
1076  ERR_NG << "division by zero with divide= in ability/weapon special " << effect_id << std::endl;
1077  }
1078  else {
1079  int divide = int(v->to_double() * 100);
1080  std::map<std::string,individual_effect>::iterator div_effect = values_div.find(effect_id);
1081  if(div_effect == values_div.end() || divide > div_effect->second.value) {
1082  values_div[effect_id].set(DIV, divide, ability.first, ability.second);
1083  }
1084  }
1085  }
1086  }
1087 
1088  if(value_is_set && set_effect.type != NOT_USED) {
1089  effect_list_.push_back(set_effect);
1090  }
1091 
1092  /* Do multiplication with floating point values rather than integers
1093  * We want two places of precision for each multiplier
1094  * Using integers multiplied by 100 to keep precision causes overflow
1095  * after 3-4 abilities for 32-bit values and ~8 for 64-bit
1096  * Avoiding the overflow by dividing after each step introduces rounding errors
1097  * that may vary depending on the order effects are applied
1098  * As the final values are likely <1000 (always true for mainline), loss of less significant digits is not an issue
1099  */
1100  double multiplier = 1.0;
1101  double divisor = 1.0;
1102  std::map<std::string,individual_effect>::const_iterator e, e_end;
1103  for (e = values_mul.begin(), e_end = values_mul.end(); e != e_end; ++e) {
1104  multiplier *= e->second.value/100.0;
1105  effect_list_.push_back(e->second);
1106  }
1107  for (e = values_div.begin(), e_end = values_div.end(); e != e_end; ++e) {
1108  divisor *= e->second.value/100.0;
1109  effect_list_.push_back(e->second);
1110  }
1111  int addition = 0;
1112  for (e = values_add.begin(), e_end = values_add.end(); e != e_end; ++e) {
1113  addition += e->second.value;
1114  effect_list_.push_back(e->second);
1115  }
1116 
1117  composite_value_ = int((value_set + addition) * multiplier / divisor);
1118 }
1119 
1120 } // end namespace unit_abilities
1121 
const t_string & name() const
Definition: attack_type.hpp:35
child_itors child_range(const std::string &key)
Definition: config.cpp:613
std::vector< individual_effect > effect_list_
Definition: abilities.hpp:57
::tod_manager * tod_manager
Definition: resources.cpp:31
unit_iterator end()
Definition: map.hpp:311
#define ERR_NG
Definition: abilities.cpp:32
virtual const display_context & get_disp_context() const =0
bool ability_affects_adjacent(const std::string &ability, const config &cfg, int dir, const map_location &loc, const unit &from) const
Definition: abilities.cpp:383
bool matches_filter(const config &filter) const
Returns whether or not *this matches the given filter.
Definition: unit.hpp:95
void set_specials_context(const map_location &unit_loc, const map_location &other_loc, bool attacking, const attack_type *other_attack) const
Sets the context under which specials will be checked for being active.
Definition: abilities.cpp:677
const t_string & name() const
The unit name for display.
Definition: unit.hpp:158
int modified_damage(bool is_backstab) const
Returns the damage per attack of this weapon, considering specials.
Definition: abilities.cpp:743
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
std::vector< boost::tuple< t_string, t_string, t_string > > ability_tooltips(std::vector< bool > *active_list=nullptr) const
Tuple of: neutral ability name, gendered ability name, description.
Definition: abilities.cpp:261
t_string t_str() const
Definition: config.cpp:358
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.hpp:274
void set(value_modifier t, int val, const config *abil, const map_location &l)
Definition: abilities.cpp:984
void set_specials_context_for_listing() const
Definition: abilities.cpp:704
map_location other_loc_
GLuint const GLfloat * val
Definition: glew.h:2614
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: abilities.cpp:426
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:220
int side() const
Definition: unit.hpp:201
GLuint divisor
Definition: glew.h:2358
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:128
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Returns true if the unit is currently under effect by an ability with this given TAG NAME...
Definition: abilities.cpp:129
-file sdl_utils.hpp
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:649
bool empty() const
Definition: config.cpp:1105
bool empty() const
Tests for an attribute that either was never set or was set to "".
Definition: config.cpp:375
GLdouble GLdouble t
Definition: glew.h:1366
std::vector< unit_ability > cfgs_
Definition: unit.hpp:91
std::pair< const_child_iterator, const_child_iterator > const_child_itors
Definition: config.hpp:214
Variant for storing WML attributes.
Definition: config.hpp:223
int num_attacks() const
Definition: attack_type.hpp:46
bool filter_base_matches(const config &cfg, int def)
Definition: abilities.cpp:992
bool match(const map_location &loc) const
Definition: filter.cpp:364
GLdouble l
Definition: glew.h:6966
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:552
bool blank() const
Tests for an attribute that was never set.
Definition: config.cpp:367
static std::vector< team > *& teams
Definition: team.cpp:50
GLuint64EXT * result
Definition: glew.h:10727
static std::string sub(const std::string &s)
Private function to surround an argument with brackets.
std::vector< team > * teams
Definition: resources.cpp:29
bool valid() const
Definition: location.hpp:69
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
Definition: filter.hpp:67
filter_context * filter_con
Definition: resources.cpp:23
const GLdouble * v
Definition: glew.h:1359
GLsizei const GLfloat * value
Definition: glew.h:1817
all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:1127
game_board * gameboard
Definition: resources.cpp:20
bool ability_affects_self(const std::string &ability, const config &cfg, const map_location &loc) const
Definition: abilities.cpp:411
void flatten(const bool flat_tod=true)
Definition: filter.hpp:66
effect(const unit_ability_list &list, int def, bool backstab)
Definition: abilities.cpp:1011
config specials_
static const map_location & null_location()
Definition: location.hpp:195
unit_race::GENDER gender_
Definition: unit.hpp:470
GLfloat GLfloat p
Definition: glew.h:12766
bool has_ability_type(const std::string &ability) const
Definition: abilities.cpp:419
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
GLuint GLuint GLsizei count
Definition: glew.h:1221
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:865
config abilities_
Definition: unit.hpp:524
Encapsulates the map of the game.
Definition: location.hpp:38
bool empty() const
Definition: unit.hpp:80
GLuint res
Definition: glew.h:9258
virtual const std::vector< team > & teams() const =0
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:262
bool is_for_listing_
std::vector< std::pair< int, int > > parse_ranges(std::string const &str)
std::pair< const config *, map_location > unit_ability
The things contained within a unit_ability_list.
Definition: unit.hpp:43
GLuint index
Definition: glew.h:1782
size_t i
Definition: function.cpp:1057
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:717
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
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:587
unit_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Definition: abilities.cpp:168
GLuint const GLchar * name
Definition: glew.h:1782
int damage() const
Definition: attack_type.hpp:45
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glew.h:3448
std::pair< int, map_location > lowest(const std::string &key, int def=0) const
Definition: abilities.cpp:457
bool find(E event, F functor)
Tests whether an event handler is available.
bool ability_active(const std::string &ability, const config &cfg, const map_location &loc) const
Definition: abilities.cpp:309
config & child(const std::string &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:658
A variable-expanding proxy for the config class.
Definition: variable.hpp:36
Standard logging facilities (interface).
Container associating units to locations.
Definition: map.hpp:90
int to_int(int def=0) const
Definition: config.cpp:308
#define e
void push_back(const unit_ability &ability)
Definition: unit.hpp:87
int get_composite_value() const
Definition: abilities.hpp:50
unit_iterator find(size_t id)
Definition: map.cpp:285
bool valid() const
Definition: map.hpp:229
const std::string weapon
GLuint GLdouble GLdouble u2
Definition: glew.h:2972
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
map_location loc_
Definition: unit.hpp:440
const attack_type * other_attack_
std::vector< std::string > get_ability_list() const
Definition: abilities.cpp:210
GLsizei const GLcharARB ** string
Definition: glew.h:4503
int side_
Definition: unit.hpp:468
unit_map * units
Definition: resources.cpp:35
bool empty() const
Definition: tstring.hpp:166
map_location self_loc_
static lg::log_domain log_engine("engine")
std::vector< std::pair< t_string, t_string > > special_tooltips(std::vector< bool > *active_list=nullptr) const
Returns a vector of names and descriptions for the specials of *this.
Definition: abilities.cpp:612