The Battle for Wesnoth  1.15.6+dev
attack.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.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  * Various functions that implement attacks and attack calculations.
18  * Unit advancements are also included, as they usually occur as a
19  * result of combat.
20  */
21 
22 #pragma once
23 
25 #include "attack_prediction.hpp"
26 #include "units/alignment.hpp"
27 #include "units/ptr.hpp"
28 
29 #include <vector>
30 
31 struct map_location;
32 class team;
33 struct time_of_day;
34 class unit_type;
35 class unit;
36 class unit_map;
37 class gamemap;
38 
39 /** Calculates the number of blows resulting from swarm. */
40 inline unsigned swarm_blows(unsigned min_blows, unsigned max_blows, unsigned hp, unsigned max_hp)
41 {
42  return hp >= max_hp
43  ? max_blows
44  : max_blows < min_blows
45  ? min_blows - (min_blows - max_blows) * hp / max_hp
46  : min_blows + (max_blows - min_blows) * hp / max_hp;
47 }
48 
49 /** Structure describing the statistics of a unit involved in the battle. */
51 {
52  const_attack_ptr weapon; /**< The weapon used by the unit to attack the opponent, or nullptr if there is none. */
53  int attack_num; /**< Index into unit->attacks() or -1 for none. */
54  bool is_attacker; /**< True if the unit is the attacker. */
55  bool is_poisoned; /**< True if the unit is poisoned at the beginning of the battle. */
56  bool is_slowed; /**< True if the unit is slowed at the beginning of the battle. */
57  bool slows; /**< Attack slows opponent when it hits. */
58  bool drains; /**< Attack drains opponent when it hits. */
59  bool petrifies; /**< Attack petrifies opponent when it hits. */
60  bool plagues; /**< Attack turns opponent into a zombie when fatal. */
61  bool poisons; /**< Attack poisons opponent when it hits. */
62  bool backstab_pos; /**<
63  * True if the attacker is in *position* to backstab the defender (this is used to
64  * determine whether to apply the backstab bonus in case the attacker has backstab).
65  */
66  bool swarm; /**< Attack has swarm special. */
67  bool firststrike; /**< Attack has firststrike special. */
68  bool disable; /**< Attack has disable special. */
69  unsigned int experience, max_experience;
70  unsigned int level;
71 
72  unsigned int rounds; /**< Berserk special can force us to fight more than one round. */
73  unsigned int hp; /**< Hitpoints of the unit at the beginning of the battle. */
74  unsigned int max_hp; /**< Maximum hitpoints of the unit. */
75  unsigned int chance_to_hit; /**< Effective chance to hit as a percentage (all factors accounted for). */
76  int damage; /**< Effective damage of the weapon (all factors accounted for). */
77  int slow_damage; /**< Effective damage if unit becomes slowed (== damage, if already slowed) */
78  int drain_percent; /**< Percentage of damage recovered as health */
79  int drain_constant; /**< Base HP drained regardless of damage dealt */
80  unsigned int num_blows; /**< Effective number of blows, takes swarm into account. */
81  unsigned int swarm_min; /**< Minimum number of blows with swarm (equal to num_blows if swarm isn't used). */
82  unsigned int swarm_max; /**< Maximum number of blows with swarm (equal to num_blows if swarm isn't used). */
83 
84  std::string plague_type; /**< The plague type used by the attack, if any. */
85 
87  const map_location& u_loc,
88  int u_attack_num,
89  bool attacking,
91  const map_location& opp_loc,
92  const_attack_ptr opp_weapon,
93  const unit_map& units);
94 
95  /** Used by AI for combat analysis, and by statistics_dialog */
97  const_attack_ptr att_weapon,
98  bool attacking,
99  const unit_type* opp_type,
100  const_attack_ptr opp_weapon,
101  unsigned int opp_terrain_defense,
102  int lawful_bonus = 0);
103 
105  {
106  }
107 
108  /// Calculates the number of blows we would have if we had @a new_hp
109  // instead of the recorded hp.
110  unsigned int calc_blows(unsigned new_hp) const
111  {
112  return swarm_blows(swarm_min, swarm_max, new_hp, max_hp);
113  }
114 
115 #if defined(BENCHMARK) || defined(CHECK)
116  /**
117  * Special constructor for the stand-alone version of attack_prediction.cpp.
118  * (This hardcodes some standard abilities for testing purposes.)
119  */
121  int blows,
122  int hitpoints,
123  int maximum_hp,
124  int hit_chance,
125  bool drain,
126  bool slows,
127  bool slowed,
128  bool berserk,
129  bool first,
130  bool do_swarm)
131  : weapon(nullptr) // Not used in attack prediction.
132  , attack_num(0) // Not used in attack prediction.
133  , is_attacker(true) // Not used in attack prediction.
134  , is_poisoned(false)
135  , is_slowed(slowed)
136  , slows(slows)
137  , drains(drain)
138  , petrifies(false)
139  , plagues(false)
140  , poisons(false)
141  , backstab_pos(false)
142  , swarm(do_swarm)
143  , firststrike(first)
144  , disable(false)
145  , experience(0) // No units should advance in the attack prediction tests.
146  , max_experience(50) // No units should advance in the attack prediction tests.
147  , level(1) // No units should advance in the attack prediction tests.
148  , rounds(berserk ? 30 : 1)
149  , hp(std::max<int>(0, hitpoints))
150  , max_hp(std::max<int>(1, maximum_hp))
151  , chance_to_hit(hit_chance)
152  , damage(std::max(0, dmg))
153  , slow_damage(round_damage(damage, 1, 2))
154  , drain_percent(drain ? 50 : 0)
155  , drain_constant(0)
156  , num_blows(do_swarm ? blows * hp / max_hp : blows)
157  , swarm_min(do_swarm ? 0 : blows)
158  , swarm_max(blows)
159  , plague_type()
160  {
161  if(slowed) {
162  damage = slow_damage;
163  }
164 
165  if(hp > max_hp) {
166  hp = max_hp; // Keeps the prob_matrix from going out of bounds.
167  }
168  }
169 #endif
170 };
171 
172 /** Computes the statistics of a battle between an attacker and a defender unit. */
174 {
175 public:
176  /**
177  * If no attacker_weapon is given, we select the best one,
178  * based on harm_weight (1.0 means 1 hp lost counters 1 hp damage,
179  * 0.0 means we ignore harm weight).
180  * prev_def is for predicting multiple attacks against a defender.
181  */
182  battle_context(const unit_map& units,
183  const map_location& attacker_loc,
184  const map_location& defender_loc,
185  int attacker_weapon = -1,
186  int defender_weapon = -1,
187  double aggression = 0.0,
188  const combatant* prev_def = nullptr,
189  unit_const_ptr attacker_ptr = unit_const_ptr(),
190  unit_const_ptr defender_ptr = unit_const_ptr());
191 
192  /** Used by the AI which caches battle_context_unit_stats */
194 
195  battle_context(battle_context&& other) = default;
196 
197  battle_context& operator=(battle_context&& other) = default;
198 
199  /** This method returns the statistics of the attacker. */
201  {
202  return *attacker_stats_;
203  }
204 
205  /** This method returns the statistics of the defender. */
207  {
208  return *defender_stats_;
209  }
210 
211  /** Get the simulation results. */
212  const combatant& get_attacker_combatant(const combatant* prev_def = nullptr);
213  const combatant& get_defender_combatant(const combatant* prev_def = nullptr);
214 
215  /** Given this harm_weight, is this attack better than that? */
216  bool better_attack(class battle_context& that, double harm_weight);
217  /** Given this harm_weight, is this attack better than that? */
218  bool better_defense(class battle_context& that, double harm_weight);
219 
220  static bool better_combat(const combatant& us_a,
221  const combatant& them_a,
222  const combatant& us_b,
223  const combatant& them_b,
224  double harm_weight);
225 
226  void simulate(const combatant* prev_def);
227 private:
229  nonempty_unit_const_ptr attacker,
230  const map_location& attacker_loc,
231  int attacker_weapon,
232  nonempty_unit_const_ptr defender,
233  const map_location& defender_loc,
234  int defender_weapon,
235  const unit_map& units);
236 
237  static battle_context choose_attacker_weapon(nonempty_unit_const_ptr attacker,
238  nonempty_unit_const_ptr defender,
239  const unit_map& units,
240  const map_location& attacker_loc,
241  const map_location& defender_loc,
242  double harm_weight,
243  const combatant* prev_def);
244 
245  static battle_context choose_defender_weapon(nonempty_unit_const_ptr attacker,
246  nonempty_unit_const_ptr defender,
247  unsigned attacker_weapon,
248  const unit_map& units,
249  const map_location& attacker_loc,
250  const map_location& defender_loc,
251  const combatant* prev_def);
252 
253  /** Statistics of the units. */
254  std::unique_ptr<battle_context_unit_stats> attacker_stats_;
255  std::unique_ptr<battle_context_unit_stats> defender_stats_;
256 
257  /** Outcome of simulated fight. */
258  std::unique_ptr<combatant> attacker_combatant_;
259  std::unique_ptr<combatant> defender_combatant_;
260 };
261 
262 /** Performs an attack. */
263 void attack_unit(const map_location& attacker,
264  const map_location& defender,
265  int attack_with,
266  int defend_with,
267  bool update_display = true);
268 
269 /** Performs an attack, and advanced the units afterwards */
270 void attack_unit_and_advance(const map_location& attacker,
271  const map_location& defender,
272  int attack_with,
273  int defend_with,
274  bool update_display = true);
275 
276 /**
277  * Tests if the unit at loc is currently affected by leadership.
278  * (i.e. has a higher-level unit with the 'leadership' ability next to it).
279  *
280  * Returns the bonus percentage (possibly 0 if there's no leader adjacent).
281  */
282 int under_leadership(const unit &u, const map_location& loc, const_attack_ptr weapon = nullptr, const_attack_ptr opp_weapon = nullptr);
283 
284 /**
285  * Returns the amount that a unit's damage should be multiplied by
286  * due to the current time of day.
287  */
288 int combat_modifier(const unit_map& units,
289  const gamemap& map,
290  const map_location& loc,
291  UNIT_ALIGNMENT alignment,
292  bool is_fearless);
293 
294 /**
295  * Returns the amount that a unit's damage should be multiplied by
296  * due to the current time of day.
297  */
298 int combat_modifier(const time_of_day& effective_tod,
299  UNIT_ALIGNMENT alignment,
300  bool is_fearless);
301 
302 /**
303  * Returns the amount that a unit's damage should be multiplied by
304  * due to a given lawful_bonus.
305  */
306 int generic_combat_modifier(int lawful_bonus, UNIT_ALIGNMENT alignment, bool is_fearless, int max_liminal_bonus);
307 /**
308  * Function to check if an attack will satisfy the requirements for backstab.
309  * Input:
310  * - the location from which the attack will occur,
311  * - the defending unit location,
312  * - the list of units on the map and
313  * - the list of teams.
314  * The defender and opposite units should be in place already.
315  * The attacking unit doesn't need to be, but if it isn't,
316  * an external check should be made to make sure the opposite unit
317  * isn't also the attacker.
318  */
319 bool backstab_check(const map_location& attacker_loc,
320  const map_location& defender_loc,
321  const unit_map& units,
322  const std::vector<team>& teams);
const_attack_ptr weapon
The weapon used by the unit to attack the opponent, or nullptr if there is none.
Definition: attack.hpp:52
std::unique_ptr< combatant > attacker_combatant_
Outcome of simulated fight.
Definition: attack.hpp:258
unsigned int calc_blows(unsigned new_hp) const
Calculates the number of blows we would have if we had new_hp.
Definition: attack.hpp:110
int under_leadership(const unit &u, const map_location &loc, const_attack_ptr weapon=nullptr, const_attack_ptr opp_weapon=nullptr)
Tests if the unit at loc is currently affected by leadership.
Definition: attack.cpp:1584
std::string plague_type
The plague type used by the attack, if any.
Definition: attack.hpp:84
This class represents a single unit of a specific type.
Definition: unit.hpp:129
const battle_context_unit_stats & get_defender_stats() const
This method returns the statistics of the defender.
Definition: attack.hpp:206
unsigned int hp
Hitpoints of the unit at the beginning of the battle.
Definition: attack.hpp:73
bool is_slowed
True if the unit is slowed at the beginning of the battle.
Definition: attack.hpp:56
bool slows
Attack slows opponent when it hits.
Definition: attack.hpp:57
int drain_constant
Base HP drained regardless of damage dealt.
Definition: attack.hpp:79
unsigned int chance_to_hit
Effective chance to hit as a percentage (all factors accounted for).
Definition: attack.hpp:75
A single unit type that the player may recruit.
Definition: types.hpp:44
void attack_unit(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display=true)
Performs an attack.
Definition: attack.cpp:1555
bool poisons
Attack poisons opponent when it hits.
Definition: attack.hpp:61
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:29
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
bool backstab_pos
True if the attacker is in position to backstab the defender (this is used to determine whether to ap...
Definition: attack.hpp:62
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
int damage
Effective damage of the weapon (all factors accounted for).
Definition: attack.hpp:76
unsigned int level
Definition: attack.hpp:70
void attack_unit_and_advance(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display=true)
Performs an attack, and advanced the units afterwards.
Definition: attack.cpp:1565
unsigned int rounds
Berserk special can force us to fight more than one round.
Definition: attack.hpp:72
unsigned int swarm_min
Minimum number of blows with swarm (equal to num_blows if swarm isn&#39;t used).
Definition: attack.hpp:81
int generic_combat_modifier(int lawful_bonus, UNIT_ALIGNMENT alignment, bool is_fearless, int max_liminal_bonus)
Returns the amount that a unit&#39;s damage should be multiplied by due to a given lawful_bonus.
Definition: attack.cpp:1611
std::unique_ptr< battle_context_unit_stats > defender_stats_
Definition: attack.hpp:255
bool plagues
Attack turns opponent into a zombie when fatal.
Definition: attack.hpp:60
int combat_modifier(const unit_map &units, const gamemap &map, const map_location &loc, UNIT_ALIGNMENT alignment, bool is_fearless)
Returns the amount that a unit&#39;s damage should be multiplied by due to the current time of day...
Definition: attack.cpp:1591
Encapsulates the map of the game.
Definition: map.hpp:36
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:173
int attack_num
Index into unit->attacks() or -1 for none.
Definition: attack.hpp:53
Structure describing the statistics of a unit involved in the battle.
Definition: attack.hpp:50
std::unique_ptr< battle_context_unit_stats > attacker_stats_
Statistics of the units.
Definition: attack.hpp:254
bool backstab_check(const map_location &attacker_loc, const map_location &defender_loc, const unit_map &units, const std::vector< team > &teams)
Function to check if an attack will satisfy the requirements for backstab.
Definition: attack.cpp:1639
Encapsulates the map of the game.
Definition: location.hpp:42
int round_damage(int base_damage, int bonus, int divisor)
round (base_damage * bonus / divisor) to the closest integer, but up or down towards base_damage ...
Definition: math.hpp:79
unsigned swarm_blows(unsigned min_blows, unsigned max_blows, unsigned hp, unsigned max_hp)
Calculates the number of blows resulting from swarm.
Definition: attack.hpp:40
All combat-related info.
bool swarm
Attack has swarm special.
Definition: attack.hpp:66
int slow_damage
Effective damage if unit becomes slowed (== damage, if already slowed)
Definition: attack.hpp:77
std::unique_ptr< combatant > defender_combatant_
Definition: attack.hpp:259
unsigned int experience
Definition: attack.hpp:69
bool disable
Attack has disable special.
Definition: attack.hpp:68
bool firststrike
Attack has firststrike special.
Definition: attack.hpp:67
bool is_poisoned
True if the unit is poisoned at the beginning of the battle.
Definition: attack.hpp:55
int drain_percent
Percentage of damage recovered as health.
Definition: attack.hpp:78
Container associating units to locations.
Definition: map.hpp:99
unsigned int num_blows
Effective number of blows, takes swarm into account.
Definition: attack.hpp:80
unsigned int max_hp
Maximum hitpoints of the unit.
Definition: attack.hpp:74
bool is_attacker
True if the unit is the attacker.
Definition: attack.hpp:54
unsigned int max_experience
Definition: attack.hpp:69
bool petrifies
Attack petrifies opponent when it hits.
Definition: attack.hpp:59
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:36
battle_context_unit_stats(nonempty_unit_const_ptr u, const map_location &u_loc, int u_attack_num, bool attacking, nonempty_unit_const_ptr opp, const map_location &opp_loc, const_attack_ptr opp_weapon, const unit_map &units)
Definition: attack.cpp:75
bool drains
Attack drains opponent when it hits.
Definition: attack.hpp:58
const battle_context_unit_stats & get_attacker_stats() const
This method returns the statistics of the attacker.
Definition: attack.hpp:200
unsigned int swarm_max
Maximum number of blows with swarm (equal to num_blows if swarm isn&#39;t used).
Definition: attack.hpp:82