The Battle for Wesnoth  1.17.0-dev
attack.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Various functions that implement attacks and attack calculations.
19  * Unit advancements are also included, as they usually occur as a
20  * result of combat.
21  */
22 
23 #pragma once
24 
26 #include "attack_prediction.hpp"
27 #include "units/alignment.hpp"
28 #include "units/ptr.hpp"
29 
30 #include <vector>
31 
32 struct map_location;
33 class team;
34 struct time_of_day;
35 class unit_type;
36 class unit;
37 class unit_map;
38 class gamemap;
39 
40 /** Calculates the number of blows resulting from swarm. */
41 inline unsigned swarm_blows(unsigned min_blows, unsigned max_blows, unsigned hp, unsigned max_hp)
42 {
43  return hp >= max_hp
44  ? max_blows
45  : max_blows < min_blows
46  ? min_blows - (min_blows - max_blows) * hp / max_hp
47  : min_blows + (max_blows - min_blows) * hp / max_hp;
48 }
49 
50 /** Structure describing the statistics of a unit involved in the battle. */
52 {
53  const_attack_ptr weapon; /**< The weapon used by the unit to attack the opponent, or nullptr if there is none. */
54  int attack_num; /**< Index into unit->attacks() or -1 for none. */
55  bool is_attacker; /**< True if the unit is the attacker. */
56  bool is_poisoned; /**< True if the unit is poisoned at the beginning of the battle. */
57  bool is_slowed; /**< True if the unit is slowed at the beginning of the battle. */
58  bool slows; /**< Attack slows opponent when it hits. */
59  bool drains; /**< Attack drains opponent when it hits. */
60  bool petrifies; /**< Attack petrifies opponent when it hits. */
61  bool plagues; /**< Attack turns opponent into a zombie when fatal. */
62  bool poisons; /**< Attack poisons opponent when it hits. */
63  bool backstab_pos; /**<
64  * True if the attacker is in *position* to backstab the defender (this is used to
65  * determine whether to apply the backstab bonus in case the attacker has backstab).
66  */
67  bool swarm; /**< Attack has swarm special. */
68  bool firststrike; /**< Attack has firststrike special. */
69  bool disable; /**< Attack has disable special. */
70  unsigned int experience, max_experience;
71  unsigned int level;
72 
73  unsigned int rounds; /**< Berserk special can force us to fight more than one round. */
74  unsigned int hp; /**< Hitpoints of the unit at the beginning of the battle. */
75  unsigned int max_hp; /**< Maximum hitpoints of the unit. */
76  unsigned int chance_to_hit; /**< Effective chance to hit as a percentage (all factors accounted for). */
77  int damage; /**< Effective damage of the weapon (all factors accounted for). */
78  int slow_damage; /**< Effective damage if unit becomes slowed (== damage, if already slowed) */
79  int drain_percent; /**< Percentage of damage recovered as health */
80  int drain_constant; /**< Base HP drained regardless of damage dealt */
81  unsigned int num_blows; /**< Effective number of blows, takes swarm into account. */
82  unsigned int swarm_min; /**< Minimum number of blows with swarm (equal to num_blows if swarm isn't used). */
83  unsigned int swarm_max; /**< Maximum number of blows with swarm (equal to num_blows if swarm isn't used). */
84 
85  std::string plague_type; /**< The plague type used by the attack, if any. */
86 
88  const map_location& u_loc,
89  int u_attack_num,
90  bool attacking,
92  const map_location& opp_loc,
93  const_attack_ptr opp_weapon,
94  const unit_map& units);
95 
96  /** Used by AI for combat analysis, and by statistics_dialog */
98  const_attack_ptr att_weapon,
99  bool attacking,
100  const unit_type* opp_type,
101  const_attack_ptr opp_weapon,
102  unsigned int opp_terrain_defense,
103  int lawful_bonus = 0);
104 
106  {
107  }
108 
109  /** Calculates the number of blows we would have if we had @a new_hp 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:53
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 instead of the recorded 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:1585
std::string plague_type
The plague type used by the attack, if any.
Definition: attack.hpp:85
This class represents a single unit of a specific type.
Definition: unit.hpp:121
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:74
bool is_slowed
True if the unit is slowed at the beginning of the battle.
Definition: attack.hpp:57
bool slows
Attack slows opponent when it hits.
Definition: attack.hpp:58
int drain_constant
Base HP drained regardless of damage dealt.
Definition: attack.hpp:80
unsigned int chance_to_hit
Effective chance to hit as a percentage (all factors accounted for).
Definition: attack.hpp:76
A single unit type that the player may recruit.
Definition: types.hpp:45
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:1556
bool poisons
Attack poisons opponent when it hits.
Definition: attack.hpp:62
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:56
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:63
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
int damage
Effective damage of the weapon (all factors accounted for).
Definition: attack.hpp:77
unsigned int level
Definition: attack.hpp:71
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:1566
unsigned int rounds
Berserk special can force us to fight more than one round.
Definition: attack.hpp:73
unsigned int swarm_min
Minimum number of blows with swarm (equal to num_blows if swarm isn&#39;t used).
Definition: attack.hpp:82
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:1612
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:61
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:1592
Encapsulates the map of the game.
Definition: map.hpp:171
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:54
Structure describing the statistics of a unit involved in the battle.
Definition: attack.hpp:51
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:1640
Encapsulates the map of the game.
Definition: location.hpp:38
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:80
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:41
All combat-related info.
bool swarm
Attack has swarm special.
Definition: attack.hpp:67
int slow_damage
Effective damage if unit becomes slowed (== damage, if already slowed)
Definition: attack.hpp:78
std::unique_ptr< combatant > defender_combatant_
Definition: attack.hpp:259
unsigned int experience
Definition: attack.hpp:70
bool disable
Attack has disable special.
Definition: attack.hpp:69
bool firststrike
Attack has firststrike special.
Definition: attack.hpp:68
bool is_poisoned
True if the unit is poisoned at the beginning of the battle.
Definition: attack.hpp:56
int drain_percent
Percentage of damage recovered as health.
Definition: attack.hpp:79
Container associating units to locations.
Definition: map.hpp:98
unsigned int num_blows
Effective number of blows, takes swarm into account.
Definition: attack.hpp:81
unsigned int max_hp
Maximum hitpoints of the unit.
Definition: attack.hpp:75
bool is_attacker
True if the unit is the attacker.
Definition: attack.hpp:55
unsigned int max_experience
Definition: attack.hpp:70
bool petrifies
Attack petrifies opponent when it hits.
Definition: attack.hpp:60
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:34
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:78
bool drains
Attack drains opponent when it hits.
Definition: attack.hpp:59
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:83