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
24 #include "ai/lua/aspect_advancements.hpp"
25 #include "attack_prediction.hpp"
26 #include "units/types.hpp"
27
28 #include <vector>
29
30 struct map_location;
31 class team;
32 struct time_of_day;
33 class unit;
34 class unit_map;
35 class gamemap;
36
37 /** Calculates the number of blows resulting from swarm. */
swarm_blows(unsigned min_blows,unsigned max_blows,unsigned hp,unsigned max_hp)38 inline unsigned swarm_blows(unsigned min_blows, unsigned max_blows, unsigned hp, unsigned max_hp)
39 {
40 return hp >= max_hp
41 ? max_blows
42 : max_blows < min_blows
43 ? min_blows - (min_blows - max_blows) * hp / max_hp
44 : min_blows + (max_blows - min_blows) * hp / max_hp;
45 }
46
47 /** Structure describing the statistics of a unit involved in the battle. */
48 struct battle_context_unit_stats
49 {
50 const_attack_ptr weapon; /**< The weapon used by the unit to attack the opponent, or nullptr if there is none. */
51 int attack_num; /**< Index into unit->attacks() or -1 for none. */
52 bool is_attacker; /**< True if the unit is the attacker. */
53 bool is_poisoned; /**< True if the unit is poisoned at the beginning of the battle. */
54 bool is_slowed; /**< True if the unit is slowed at the beginning of the battle. */
55 bool slows; /**< Attack slows opponent when it hits. */
56 bool drains; /**< Attack drains opponent when it hits. */
57 bool petrifies; /**< Attack petrifies opponent when it hits. */
58 bool plagues; /**< Attack turns opponent into a zombie when fatal. */
59 bool poisons; /**< Attack poisons opponent when it hits. */
60 bool backstab_pos; /**<
61 * True if the attacker is in *position* to backstab the defender (this is used to
62 * determine whether to apply the backstab bonus in case the attacker has backstab).
63 */
64 bool swarm; /**< Attack has swarm special. */
65 bool firststrike; /**< Attack has firststrike special. */
66 bool disable; /**< Attack has disable special. */
67 unsigned int experience, max_experience;
68 unsigned int level;
69
70 unsigned int rounds; /**< Berserk special can force us to fight more than one round. */
71 unsigned int hp; /**< Hitpoints of the unit at the beginning of the battle. */
72 unsigned int max_hp; /**< Maximum hitpoints of the unit. */
73 unsigned int chance_to_hit; /**< Effective chance to hit as a percentage (all factors accounted for). */
74 int damage; /**< Effective damage of the weapon (all factors accounted for). */
75 int slow_damage; /**< Effective damage if unit becomes slowed (== damage, if already slowed) */
76 int drain_percent; /**< Percentage of damage recovered as health */
77 int drain_constant; /**< Base HP drained regardless of damage dealt */
78 unsigned int num_blows; /**< Effective number of blows, takes swarm into account. */
79 unsigned int swarm_min; /**< Minimum number of blows with swarm (equal to num_blows if swarm isn't used). */
80 unsigned int swarm_max; /**< Maximum number of blows with swarm (equal to num_blows if swarm isn't used). */
81
82 std::string plague_type; /**< The plague type used by the attack, if any. */
83
84 battle_context_unit_stats(const unit& u,
85 const map_location& u_loc,
86 int u_attack_num,
87 bool attacking,
88 const unit& opp,
89 const map_location& opp_loc,
90 const_attack_ptr opp_weapon,
91 const unit_map& units);
92
93 /** Used by AI for combat analysis */
94 battle_context_unit_stats(const unit_type* u_type,
95 const_attack_ptr att_weapon,
96 bool attacking,
97 const unit_type* opp_type,
98 const_attack_ptr opp_weapon,
99 unsigned int opp_terrain_defense,
100 int lawful_bonus = 0);
101
~battle_context_unit_statsbattle_context_unit_stats102 ~battle_context_unit_stats()
103 {
104 }
105
106 /// Calculates the number of blows we would have if we had @a new_hp
107 // instead of the recorded hp.
calc_blowsbattle_context_unit_stats108 unsigned int calc_blows(unsigned new_hp) const
109 {
110 return swarm_blows(swarm_min, swarm_max, new_hp, max_hp);
111 }
112
113 #if defined(BENCHMARK) || defined(CHECK)
114 /**
115 * Special constructor for the stand-alone version of attack_prediction.cpp.
116 * (This hardcodes some standard abilities for testing purposes.)
117 */
battle_context_unit_statsbattle_context_unit_stats118 battle_context_unit_stats(int dmg,
119 int blows,
120 int hitpoints,
121 int maximum_hp,
122 int hit_chance,
123 bool drain,
124 bool slows,
125 bool slowed,
126 bool berserk,
127 bool first,
128 bool do_swarm)
129 : weapon(nullptr) // Not used in attack prediction.
130 , attack_num(0) // Not used in attack prediction.
131 , is_attacker(true) // Not used in attack prediction.
132 , is_poisoned(false)
133 , is_slowed(slowed)
134 , slows(slows)
135 , drains(drain)
136 , petrifies(false)
137 , plagues(false)
138 , poisons(false)
139 , backstab_pos(false)
140 , swarm(do_swarm)
141 , firststrike(first)
142 , disable(false)
143 , experience(0) // No units should advance in the attack prediction tests.
144 , max_experience(50) // No units should advance in the attack prediction tests.
145 , level(1) // No units should advance in the attack prediction tests.
146 , rounds(berserk ? 30 : 1)
147 , hp(std::max<int>(0, hitpoints))
148 , max_hp(std::max<int>(1, maximum_hp))
149 , chance_to_hit(hit_chance)
150 , damage(std::max(0, dmg))
151 , slow_damage(round_damage(damage, 1, 2))
152 , drain_percent(drain ? 50 : 0)
153 , drain_constant(0)
154 , num_blows(do_swarm ? blows * hp / max_hp : blows)
155 , swarm_min(do_swarm ? 0 : blows)
156 , swarm_max(blows)
157 , plague_type()
158 {
159 if(slowed) {
160 damage = slow_damage;
161 }
162
163 if(hp > max_hp) {
164 hp = max_hp; // Keeps the prob_matrix from going out of bounds.
165 }
166 }
167 #endif
168 };
169
170 /** Computes the statistics of a battle between an attacker and a defender unit. */
171 class battle_context
172 {
173 public:
174 /**
175 * If no attacker_weapon is given, we select the best one,
176 * based on harm_weight (1.0 means 1 hp lost counters 1 hp damage,
177 * 0.0 means we ignore harm weight).
178 * prev_def is for predicting multiple attacks against a defender.
179 */
180 battle_context(const unit_map& units,
181 const map_location& attacker_loc,
182 const map_location& defender_loc,
183 int attacker_weapon = -1,
184 int defender_weapon = -1,
185 double aggression = 0.0,
186 const combatant* prev_def = nullptr,
187 const unit* attacker_ptr = nullptr);
188
189 /** Used by the AI which caches battle_context_unit_stats */
190 battle_context(const battle_context_unit_stats& att, const battle_context_unit_stats& def);
191
192 battle_context(const battle_context& other) = delete;
193 battle_context(battle_context&& other);
194
195 battle_context& operator=(const battle_context& other) = delete;
196 battle_context& operator=(battle_context&& other);
197
198 /** This method returns the statistics of the attacker. */
get_attacker_stats() const199 const battle_context_unit_stats& get_attacker_stats() const
200 {
201 return *attacker_stats_;
202 }
203
204 /** This method returns the statistics of the defender. */
get_defender_stats() const205 const battle_context_unit_stats& get_defender_stats() const
206 {
207 return *defender_stats_;
208 }
209
210 /** Get the simulation results. */
211 const combatant& get_attacker_combatant(const combatant* prev_def = nullptr);
212 const combatant& get_defender_combatant(const combatant* prev_def = nullptr);
213
214 /** Given this harm_weight, is this attack better than that? */
215 bool better_attack(class battle_context& that, double harm_weight);
216 /** Given this harm_weight, is this attack better than that? */
217 bool better_defense(class battle_context& that, double harm_weight);
218
219 static bool better_combat(const combatant& us_a,
220 const combatant& them_a,
221 const combatant& us_b,
222 const combatant& them_b,
223 double harm_weight);
224
225 void simulate(const combatant* prev_def);
226 private:
227 battle_context(
228 const unit& attacker,
229 const map_location& attacker_loc,
230 int attacker_weapon,
231 const unit& defender,
232 const map_location& defender_loc,
233 int defender_weapon,
234 const unit_map& units);
235
236 static battle_context choose_attacker_weapon(const unit& attacker,
237 const unit& defender,
238 const unit_map& units,
239 const map_location& attacker_loc,
240 const map_location& defender_loc,
241 double harm_weight,
242 const combatant* prev_def);
243
244 static battle_context choose_defender_weapon(const unit& attacker,
245 const unit& defender,
246 unsigned attacker_weapon,
247 const unit_map& units,
248 const map_location& attacker_loc,
249 const map_location& defender_loc,
250 const combatant* prev_def);
251
252 /** Statistics of the units. */
253 std::unique_ptr<battle_context_unit_stats> attacker_stats_;
254 std::unique_ptr<battle_context_unit_stats> defender_stats_;
255
256 /** Outcome of simulated fight. */
257 std::unique_ptr<combatant> attacker_combatant_;
258 std::unique_ptr<combatant> defender_combatant_;
259 };
260
261 /** Performs an attack. */
262 void attack_unit(const map_location& attacker,
263 const map_location& defender,
264 int attack_with,
265 int defend_with,
266 bool update_display = true);
267
268 /** Performs an attack, and advanced the units afterwards */
269 void attack_unit_and_advance(const map_location& attacker,
270 const map_location& defender,
271 int attack_with,
272 int defend_with,
273 bool update_display = true,
274 const ai::unit_advancements_aspect& ai_advancement = ai::unit_advancements_aspect());
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);
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_type::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_type::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_type::ALIGNMENT alignment, bool is_fearless);
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);
323