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