The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
attack.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[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  * Fighting.
18  */
19 
20 #include "attack.hpp"
21 
22 #include "vision.hpp"
23 
25 #include "attack_prediction.hpp"
26 #include "config_assign.hpp"
27 #include "dialogs.hpp"
28 #include "game_config.hpp"
29 #include "game_display.hpp"
30 #include "game_events/manager.hpp"
31 #include "game_events/pump.hpp"
32 #include "game_preferences.hpp"
33 #include "game_data.hpp"
34 #include "gettext.hpp"
35 #include "log.hpp"
36 #include "map/map.hpp"
37 #include "mouse_handler_base.hpp"
38 #include "play_controller.hpp"
39 #include "random_new.hpp"
40 #include "replay.hpp"
41 #include "resources.hpp"
42 #include "statistics.hpp"
43 #include "synced_checkup.hpp"
44 #include "synced_user_choice.hpp"
45 #include "team.hpp"
46 #include "tod_manager.hpp"
47 #include "units/unit.hpp"
48 #include "units/abilities.hpp"
50 #include "units/udisplay.hpp"
51 #include "units/helper.hpp"
52 #include "units/map.hpp"
53 #include "whiteboard/manager.hpp"
54 #include "wml_exception.hpp"
55 
56 static lg::log_domain log_engine("engine");
57 #define DBG_NG LOG_STREAM(debug, log_engine)
58 #define LOG_NG LOG_STREAM(info, log_engine)
59 #define WRN_NG LOG_STREAM(err, log_engine)
60 #define ERR_NG LOG_STREAM(err, log_engine)
61 
62 static lg::log_domain log_config("config");
63 #define LOG_CF LOG_STREAM(info, log_config)
64 
65 
66  /* battle_context_unit_stats */
67 
69  const map_location& u_loc, int u_attack_num, bool attacking,
70  const unit &opp, const map_location& opp_loc,
71  const attack_type *opp_weapon, const unit_map& units) :
72  weapon(nullptr),
73  attack_num(u_attack_num),
74  is_attacker(attacking),
75  is_poisoned(u.get_state(unit::STATE_POISONED)),
76  is_slowed(u.get_state(unit::STATE_SLOWED)),
77  slows(false),
78  drains(false),
79  petrifies(false),
80  plagues(false),
81  poisons(false),
82  backstab_pos(false),
83  swarm(false),
84  firststrike(false),
85  disable(false),
86  experience(u.experience()),
87  max_experience(u.max_experience()),
88  level(u.level()),
89  rounds(1),
90  hp(0),
91  max_hp(u.max_hitpoints()),
92  chance_to_hit(0),
93  damage(0),
94  slow_damage(0),
95  drain_percent(0),
96  drain_constant(0),
97  num_blows(0),
98  swarm_min(0),
99  swarm_max(0),
100  plague_type()
101 {
102  // Get the current state of the unit.
103  if (attack_num >= 0) {
104  weapon = &u.attacks()[attack_num];
105  }
106  if(u.hitpoints() < 0) {
107  LOG_CF << "Unit with " << u.hitpoints() << " hitpoints found, set to 0 for damage calculations\n";
108  hp = 0;
109  } else if(u.hitpoints() > u.max_hitpoints()) {
110  // If a unit has more hp than its maximum, the engine will fail
111  // with an assertion failure due to accessing the prob_matrix
112  // out of bounds.
113  hp = u.max_hitpoints();
114  } else {
115  hp = u.hitpoints();
116  }
117 
118  // Get the weapon characteristics, if any.
119  if (weapon) {
120  weapon->set_specials_context(u_loc, opp_loc, attacking, opp_weapon);
121  if (opp_weapon)
122  opp_weapon->set_specials_context(opp_loc, u_loc, !attacking, weapon);
123  slows = weapon->get_special_bool("slow");
124  drains = !opp.get_state("undrainable") && weapon->get_special_bool("drains");
125  petrifies = weapon->get_special_bool("petrifies");
126  poisons = !opp.get_state("unpoisonable") && weapon->get_special_bool("poison") && !opp.get_state(unit::STATE_POISONED);
127  backstab_pos = is_attacker && backstab_check(u_loc, opp_loc, units, *resources::teams);
128  rounds = weapon->get_specials("berserk").highest("value", 1).first;
129  firststrike = weapon->get_special_bool("firststrike");
130  {
131  const int distance = distance_between(u_loc, opp_loc);
132  const bool out_of_range = distance > weapon->max_range() || distance < weapon->min_range();
133  disable = weapon->get_special_bool("disable") || out_of_range;
134  }
135 
136  // Handle plague.
137  unit_ability_list plague_specials = weapon->get_specials("plague");
138  plagues = !opp.get_state("unplagueable") && !plague_specials.empty() &&
139  strcmp(opp.undead_variation().c_str(), "null") && !resources::gameboard->map().is_village(opp_loc);
140 
141  if (plagues) {
142  plague_type = (*plague_specials.front().first)["type"].str();
143  if (plague_type.empty())
144  plague_type = u.type().base_id();
145  }
146 
147  // Compute chance to hit.
149  resources::gameboard->map().get_terrain(opp_loc)) + weapon->accuracy() -
150  (opp_weapon ? opp_weapon->parry() : 0);
151  if(chance_to_hit > 100) {
152  chance_to_hit = 100;
153  }
154 
155  unit_ability_list cth_specials = weapon->get_specials("chance_to_hit");
156  unit_abilities::effect cth_effects(cth_specials, chance_to_hit, backstab_pos);
157  chance_to_hit = cth_effects.get_composite_value();
158 
159  // Compute base damage done with the weapon.
160  int base_damage = weapon->modified_damage(backstab_pos);
161 
162  // Get the damage multiplier applied to the base damage of the weapon.
163  int damage_multiplier = 100;
164  // Time of day bonus.
165  damage_multiplier += combat_modifier(resources::gameboard->units(), resources::gameboard->map(), u_loc, u.alignment(), u.is_fearless());
166  // Leadership bonus.
167  int leader_bonus = 0;
168  if (under_leadership(units, u_loc, &leader_bonus).valid())
169  damage_multiplier += leader_bonus;
170  // Resistance modifier.
171  damage_multiplier *= opp.damage_from(*weapon, !attacking, opp_loc);
172 
173  // Compute both the normal and slowed damage.
174  damage = round_damage(base_damage, damage_multiplier, 10000);
175  slow_damage = round_damage(base_damage, damage_multiplier, 20000);
176  if (is_slowed)
178 
179  // Compute drain amounts only if draining is possible.
180  if(drains) {
181  unit_ability_list drain_specials = weapon->get_specials("drains");
182 
183  // Compute the drain percent (with 50% as the base for backward compatibility)
184  unit_abilities::effect drain_percent_effects(drain_specials, 50, backstab_pos);
185  drain_percent = drain_percent_effects.get_composite_value();
186  }
187 
188  // Add heal_on_hit (the drain constant)
189  unit_ability_list heal_on_hit_specials = weapon->get_specials("heal_on_hit");
190  unit_abilities::effect heal_on_hit_effects(heal_on_hit_specials, 0, backstab_pos);
191  drain_constant += heal_on_hit_effects.get_composite_value();
192 
194 
195  // Compute the number of blows and handle swarm.
199  }
200 }
201 
203  const attack_type* att_weapon, bool attacking,
204  const unit_type* opp_type,
205  const attack_type* opp_weapon,
206  unsigned int opp_terrain_defense,
207  int lawful_bonus) :
208  weapon(att_weapon),
209  attack_num(-2), // This is and stays invalid. Always use weapon, when using this constructor.
210  is_attacker(attacking),
211  is_poisoned(false),
212  is_slowed(false),
213  slows(false),
214  drains(false),
215  petrifies(false),
216  plagues(false),
217  poisons(false),
218  backstab_pos(false),
219  swarm(false),
220  firststrike(false),
221  disable(false),
222  experience(0),
223  max_experience(0),
224  level(0),
225  rounds(1),
226  hp(0),
227  max_hp(0),
228  chance_to_hit(0),
229  damage(0),
230  slow_damage(0),
231  drain_percent(0),
232  drain_constant(0),
233  num_blows(0),
234  swarm_min(0),
235  swarm_max(0),
236  plague_type()
237 {
238  if (!u_type || !opp_type) {
239  return;
240  }
241 
242  // Get the current state of the unit.
243  if (u_type->hitpoints() < 0) {
244  hp = 0;
245  } else {
246  hp = u_type->hitpoints();
247  }
248  max_experience = u_type->experience_needed();
249  level = (u_type->level());
250  max_hp = (u_type->hitpoints());
251 
252  // Get the weapon characteristics, if any.
253  if (weapon) {
255  if (opp_weapon) {
256  opp_weapon->set_specials_context(map_location::null_location(), !attacking);
257  }
258  slows = weapon->get_special_bool("slow");
259  drains = !opp_type->musthave_status("undrainable") && weapon->get_special_bool("drains");
260  petrifies = weapon->get_special_bool("petrifies");
261  poisons = !opp_type->musthave_status("unpoisonable") && weapon->get_special_bool("poison");
262  rounds = weapon->get_specials("berserk").highest("value", 1).first;
263  firststrike = weapon->get_special_bool("firststrike");
264  disable = weapon->get_special_bool("disable");
265 
266  unit_ability_list plague_specials = weapon->get_specials("plague");
267  plagues = !opp_type->musthave_status("unplagueable") && !plague_specials.empty() &&
268  strcmp(opp_type->undead_variation().c_str(), "null");
269 
270  if (plagues) {
271  plague_type = (*plague_specials.front().first)["type"].str();
272  if (plague_type.empty()) {
273  plague_type = u_type->base_id();
274  }
275  }
276 
277  signed int cth = 100 - opp_terrain_defense + weapon->accuracy() -
278  (opp_weapon ? opp_weapon->parry() : 0);
279  cth = std::min(100, cth);
280  cth = std::max(0, cth);
281  chance_to_hit = cth;
282 
283  unit_ability_list cth_specials = weapon->get_specials("chance_to_hit");
284  unit_abilities::effect cth_effects(cth_specials, chance_to_hit, backstab_pos);
285  chance_to_hit = cth_effects.get_composite_value();
286 
287  int base_damage = weapon->modified_damage(backstab_pos);
288  int damage_multiplier = 100;
289  damage_multiplier += generic_combat_modifier(lawful_bonus, u_type->alignment(),
290  u_type->musthave_status("fearless"));
291  damage_multiplier *= opp_type->resistance_against(weapon->type(), !attacking);
292 
293  damage = round_damage(base_damage, damage_multiplier, 10000);
294  slow_damage = round_damage(base_damage, damage_multiplier, 20000);
295 
296  if (drains) {
297  unit_ability_list drain_specials = weapon->get_specials("drains");
298 
299  // Compute the drain percent (with 50% as the base for backward compatibility)
300  unit_abilities::effect drain_percent_effects(drain_specials, 50, backstab_pos);
301  drain_percent = drain_percent_effects.get_composite_value();
302  }
303 
304  // Add heal_on_hit (the drain constant)
305  unit_ability_list heal_on_hit_specials = weapon->get_specials("heal_on_hit");
306  unit_abilities::effect heal_on_hit_effects(heal_on_hit_specials, 0, backstab_pos);
307  drain_constant += heal_on_hit_effects.get_composite_value();
308 
310 
311  // Compute the number of blows and handle swarm.
315  }
316 }
317 
318  /* battle_context */
319 
321  const map_location& attacker_loc, const map_location& defender_loc,
322  int attacker_weapon, int defender_weapon, double aggression,
323  const combatant *prev_def, const unit* attacker_ptr) :
324  attacker_stats_(nullptr), defender_stats_(nullptr), attacker_combatant_(nullptr),
325  defender_combatant_(nullptr)
326 {
327  const unit &attacker = attacker_ptr ? *attacker_ptr : *units.find(attacker_loc);
328  const unit &defender = *units.find(defender_loc);
329  const double harm_weight = 1.0 - aggression;
330 
331  if (attacker_weapon == -1 && attacker.attacks().size() == 1 && attacker.attacks()[0].attack_weight() > 0 )
332  attacker_weapon = 0;
333 
334  if (attacker_weapon == -1) {
335  attacker_weapon = choose_attacker_weapon(attacker, defender, units,
336  attacker_loc, defender_loc,
337  harm_weight, &defender_weapon, prev_def);
338  } else if (defender_weapon == -1) {
339  defender_weapon = choose_defender_weapon(attacker, defender, attacker_weapon,
340  units, attacker_loc, defender_loc, prev_def);
341  }
342 
343  // If those didn't have to generate statistics, do so now.
344  if (!attacker_stats_) {
345  const attack_type *adef = nullptr;
346  const attack_type *ddef = nullptr;
347  if (attacker_weapon >= 0) {
348  VALIDATE(attacker_weapon < static_cast<int>(attacker.attacks().size()),
349  _("An invalid attacker weapon got selected."));
350  adef = &attacker.attacks()[attacker_weapon];
351  }
352  if (defender_weapon >= 0) {
353  VALIDATE(defender_weapon < static_cast<int>(defender.attacks().size()),
354  _("An invalid defender weapon got selected."));
355  ddef = &defender.attacks()[defender_weapon];
356  }
358  attacker_stats_ = new battle_context_unit_stats(attacker, attacker_loc, attacker_weapon,
359  true, defender, defender_loc, ddef, units);
360  defender_stats_ = new battle_context_unit_stats(defender, defender_loc, defender_weapon, false,
361  attacker, attacker_loc, adef, units);
362  }
363 
364  // There have been various bugs where only one of these was set
365  assert(attacker_stats_);
366  assert(defender_stats_);
367 }
368 
370  const battle_context_unit_stats &def) :
371  attacker_stats_(new battle_context_unit_stats(att)),
372  defender_stats_(new battle_context_unit_stats(def)),
373  attacker_combatant_(nullptr),
374  defender_combatant_(nullptr)
375 {
376 }
377 
379  attacker_stats_(nullptr), defender_stats_(nullptr), attacker_combatant_(nullptr),
380  defender_combatant_(nullptr)
381 {
382  *this = other;
383 }
384 
386 {
387  delete attacker_stats_;
388  delete defender_stats_;
389  delete attacker_combatant_;
390  delete defender_combatant_;
391 }
392 
394 {
395  if (&other != this) {
396  delete attacker_stats_;
397  delete defender_stats_;
398  delete attacker_combatant_;
399  delete defender_combatant_;
404  }
405  return *this;
406 }
407 
408 /** @todo FIXME: better to initialize combatant initially (move into
409  battle_context_unit_stats?), just do fight() when required. */
411 {
412  // We calculate this lazily, since AI doesn't always need it.
413  if (!attacker_combatant_) {
414  assert(!defender_combatant_);
418  }
419  return *attacker_combatant_;
420 }
421 
423 {
424  // We calculate this lazily, since AI doesn't always need it.
425  if (!defender_combatant_) {
426  assert(!attacker_combatant_);
430  }
431  return *defender_combatant_;
432 }
433 
434 // Given this harm_weight, are we better than this other context?
435 bool battle_context::better_attack(class battle_context &that, double harm_weight)
436 {
438  that.get_attacker_combatant(), that.get_defender_combatant(), harm_weight);
439 }
440 
441 // Does combat A give us a better result than combat B?
442 bool battle_context::better_combat(const combatant &us_a, const combatant &them_a,
443  const combatant &us_b, const combatant &them_b, double harm_weight)
444 {
445  double a, b;
446 
447  // Compare: P(we kill them) - P(they kill us).
448  a = them_a.hp_dist[0] - us_a.hp_dist[0] * harm_weight;
449  b = them_b.hp_dist[0] - us_b.hp_dist[0] * harm_weight;
450  if (a - b < -0.01)
451  return false;
452  if (a - b > 0.01)
453  return true;
454 
455  // Add poison to calculations
456  double poison_a_us = (us_a.poisoned) * game_config::poison_amount;
457  double poison_a_them = (them_a.poisoned) * game_config::poison_amount;
458  double poison_b_us = (us_b.poisoned) * game_config::poison_amount;
459  double poison_b_them = (them_b.poisoned) * game_config::poison_amount;
460  // Compare: damage to them - damage to us (average_hp replaces -damage)
461  a = (us_a.average_hp()-poison_a_us)*harm_weight - (them_a.average_hp()-poison_a_them);
462  b = (us_b.average_hp()-poison_b_us)*harm_weight - (them_b.average_hp()-poison_b_them);
463  if (a - b < -0.01)
464  return false;
465  if (a - b > 0.01)
466  return true;
467 
468  // All else equal: go for most damage.
469  return them_a.average_hp() < them_b.average_hp();
470 }
471 
473  const unit &defender, const unit_map& units,
474  const map_location& attacker_loc, const map_location& defender_loc,
475  double harm_weight, int *defender_weapon, const combatant *prev_def)
476 {
477  std::vector<unsigned int> choices;
478 
479  // What options does attacker have?
480  unsigned int i;
481  for (i = 0; i < attacker.attacks().size(); ++i) {
482  const attack_type &att = attacker.attacks()[i];
483  if (att.attack_weight() > 0) {
484  choices.push_back(i);
485  }
486  }
487  if (choices.empty())
488  return -1;
489  if (choices.size() == 1) {
490  *defender_weapon = choose_defender_weapon(attacker, defender, choices[0], units,
491  attacker_loc, defender_loc, prev_def);
492  const attack_type *def_weapon = *defender_weapon >= 0 ? &defender.attacks()[*defender_weapon] : nullptr;
493  attacker_stats_ = new battle_context_unit_stats(attacker, attacker_loc, choices[0],
494  true, defender, defender_loc, def_weapon, units);
495  if (attacker_stats_->disable) {
496  delete attacker_stats_;
497  attacker_stats_ = nullptr;
498  return -1;
499  }
500  const attack_type &att = attacker.attacks()[choices[0]];
501  defender_stats_ = new battle_context_unit_stats(defender, defender_loc, *defender_weapon, false,
502  attacker, attacker_loc, &att, units);
503  return choices[0];
504  }
505 
506  // Multiple options: simulate them, save best.
507  battle_context_unit_stats *best_att_stats = nullptr, *best_def_stats = nullptr;
508  combatant *best_att_comb = nullptr, *best_def_comb = nullptr;
509 
510  for (i = 0; i < choices.size(); ++i) {
511  const attack_type &att = attacker.attacks()[choices[i]];
512  int def_weapon = choose_defender_weapon(attacker, defender, choices[i], units,
513  attacker_loc, defender_loc, prev_def);
514  // If that didn't simulate, do so now.
515  if (!attacker_combatant_) {
516  const attack_type *def = nullptr;
517  if (def_weapon >= 0) {
518  def = &defender.attacks()[def_weapon];
519  }
520  attacker_stats_ = new battle_context_unit_stats(attacker, attacker_loc, choices[i],
521  true, defender, defender_loc, def, units);
522  if (attacker_stats_->disable) {
523  delete attacker_stats_;
524  attacker_stats_ = nullptr;
525  continue;
526  }
527  defender_stats_ = new battle_context_unit_stats(defender, defender_loc, def_weapon, false,
528  attacker, attacker_loc, &att, units);
532  }
533  if (!best_att_comb || better_combat(*attacker_combatant_, *defender_combatant_,
534  *best_att_comb, *best_def_comb, harm_weight)) {
535  delete best_att_comb;
536  delete best_def_comb;
537  delete best_att_stats;
538  delete best_def_stats;
539  best_att_comb = attacker_combatant_;
540  best_def_comb = defender_combatant_;
541  best_att_stats = attacker_stats_;
542  best_def_stats = defender_stats_;
543  } else {
544  delete attacker_combatant_;
545  delete defender_combatant_;
546  delete attacker_stats_;
547  delete defender_stats_;
548  }
549  attacker_combatant_ = nullptr;
550  defender_combatant_ = nullptr;
551  attacker_stats_ = nullptr;
552  defender_stats_ = nullptr;
553  }
554 
555  attacker_combatant_ = best_att_comb;
556  defender_combatant_ = best_def_comb;
557  attacker_stats_ = best_att_stats;
558  defender_stats_ = best_def_stats;
559 
560  // These currently mean the same thing, but assumptions like that have been broken before
561  if (!defender_stats_ || !attacker_stats_) {
562  return -1;
563  }
564  *defender_weapon = defender_stats_->attack_num;
565  return attacker_stats_->attack_num;
566 }
567 
568 /** @todo FIXME: Hand previous defender unit in here. */
570  const unit &defender, unsigned attacker_weapon, const unit_map& units,
571  const map_location& attacker_loc, const map_location& defender_loc,
572  const combatant *prev_def)
573 {
574  VALIDATE(attacker_weapon < attacker.attacks().size(),
575  _("An invalid attacker weapon got selected."));
576  const attack_type &att = attacker.attacks()[attacker_weapon];
577  std::vector<unsigned int> choices;
578 
579  // What options does defender have?
580  unsigned int i;
581  for (i = 0; i < defender.attacks().size(); ++i) {
582  const attack_type &def = defender.attacks()[i];
583  if (def.range() == att.range() && def.defense_weight() > 0) {
584  choices.push_back(i);
585  }
586  }
587  if (choices.empty())
588  return -1;
589  if (choices.size() == 1) {
590  const battle_context_unit_stats def_stats(defender, defender_loc,
591  choices[0], false, attacker, attacker_loc, &att, units);
592  return (def_stats.disable) ? -1 : choices[0];
593  }
594 
595  // Multiple options:
596  // First pass : get the best weight and the minimum simple rating for this weight.
597  // simple rating = number of blows * damage per blows (resistance taken in account) * cth * weight
598  // Eligible attacks for defense should have a simple rating greater or equal to this weight.
599 
600  int min_rating = 0;
601  {
602  double max_weight = 0.0;
603 
604  for (i = 0; i < choices.size(); ++i) {
605  const attack_type &def = defender.attacks()[choices[i]];
606  if (def.defense_weight() >= max_weight) {
607  const battle_context_unit_stats def_stats(defender, defender_loc,
608  choices[i], false, attacker, attacker_loc, &att, units);
609  if (def_stats.disable) continue;
610  max_weight = def.defense_weight();
611  int rating = static_cast<int>(def_stats.num_blows * def_stats.damage *
612  def_stats.chance_to_hit * def.defense_weight());
613  if (def.defense_weight() > max_weight || rating < min_rating ) {
614  min_rating = rating;
615  }
616  }
617  }
618  }
619 
620  // Multiple options: simulate them, save best.
621  for (i = 0; i < choices.size(); ++i) {
622  const attack_type &def = defender.attacks()[choices[i]];
623  battle_context_unit_stats *att_stats = new battle_context_unit_stats(attacker, attacker_loc, attacker_weapon,
624  true, defender, defender_loc, &def, units);
625  battle_context_unit_stats *def_stats = new battle_context_unit_stats(defender, defender_loc, choices[i], false,
626  attacker, attacker_loc, &att, units);
627  if (def_stats->disable) {
628  delete att_stats;
629  delete def_stats;
630  continue;
631  }
632  combatant *att_comb = new combatant(*att_stats);
633  combatant *def_comb = new combatant(*def_stats, prev_def);
634  att_comb->fight(*def_comb);
635 
636  int simple_rating = static_cast<int>(def_stats->num_blows *
637  def_stats->damage * def_stats->chance_to_hit * def.defense_weight());
638 
639  if (simple_rating >= min_rating &&
640  ( !attacker_combatant_ || better_combat(*def_comb, *att_comb, *defender_combatant_, *attacker_combatant_, 1.0) )
641  ) {
642  delete attacker_combatant_;
643  delete defender_combatant_;
644  delete attacker_stats_;
645  delete defender_stats_;
646  attacker_combatant_ = att_comb;
647  defender_combatant_ = def_comb;
648  attacker_stats_ = att_stats;
649  defender_stats_ = def_stats;
650  } else {
651  delete att_comb;
652  delete def_comb;
653  delete att_stats;
654  delete def_stats;
655  }
656  }
657 
659 }
660 
661 
662 namespace {
663  void refresh_weapon_index(int& weap_index, std::string const& weap_id, std::vector<attack_type> const& attacks)
664  {
665  if(attacks.empty()) {
666  //no attacks to choose from
667  weap_index = -1;
668  return;
669  }
670  if(weap_index >= 0 && weap_index < static_cast<int>(attacks.size()) && attacks[weap_index].id() == weap_id) {
671  //the currently selected attack fits
672  return;
673  }
674  if(!weap_id.empty()) {
675  //lookup the weapon by id
676  for(int i=0; i<static_cast<int>(attacks.size()); ++i) {
677  if(attacks[i].id() == weap_id) {
678  weap_index = i;
679  return;
680  }
681  }
682  }
683  //lookup has failed
684  weap_index = -1;
685  return;
686  }
687 
688 
689  /** Helper class for performing an attack. */
690  class attack
691  {
692  public:
693  attack(const map_location &attacker, const map_location &defender,
694  int attack_with, int defend_with, bool update_display = true);
695  ~attack();
696 
697  void perform();
698 
699  private:
700  class attack_end_exception {};
701  bool perform_hit(bool, statistics::attack_context &);
702  void fire_event(const std::string& n);
703  void refresh_bc();
704 
705  /** Structure holding unit info used in the attack action. */
706  struct unit_info
707  {
708  const map_location loc_;
709  int weapon_;
710  unit_map &units_;
711  size_t id_; /**< unit.underlying_id() */
712  std::string weap_id_;
713  int orig_attacks_;
714  int n_attacks_; /**< Number of attacks left. */
715  int cth_;
716  int damage_;
717  int xp_;
718 
719  unit_info(const map_location &loc, int weapon, unit_map &units);
720  unit &get_unit();
721  bool valid();
722 
723  std::string dump();
724  };
725 
726  /**
727  * Used in perform_hit to confirm a replay is in sync.
728  * Check OOS_error_ after this method, true if error detected.
729  */
730  void check_replay_attack_result(bool, int, int, config, unit_info&);
731 
732  void unit_killed(unit_info &, unit_info &,
734  bool);
735 
736  battle_context *bc_;
737  const battle_context_unit_stats *a_stats_;
738  const battle_context_unit_stats *d_stats_;
739 
740  int abs_n_attack_, abs_n_defend_;
741  // update_att_fog_ is not used, other than making some code simpler.
742  bool update_att_fog_, update_def_fog_, update_minimap_;
743 
744  unit_info a_, d_;
745  unit_map &units_;
746  std::ostringstream errbuf_;
747 
748  bool update_display_;
749  bool OOS_error_;
750  };
751 
752 
753  attack::unit_info::unit_info(const map_location& loc, int weapon, unit_map& units) :
754  loc_(loc),
755  weapon_(weapon),
756  units_(units),
757  id_(),
758  weap_id_(),
759  orig_attacks_(0),
760  n_attacks_(0),
761  cth_(0),
762  damage_(0),
763  xp_(0)
764  {
766  if (!i.valid()) return;
767  id_ = i->underlying_id();
768  }
769 
770  unit &attack::unit_info::get_unit()
771  {
772  unit_map::iterator i = units_.find(loc_);
773  assert(i.valid() && i->underlying_id() == id_);
774  return *i;
775  }
776 
778  {
779  unit_map::iterator i = units_.find(loc_);
780  return i.valid() && i->underlying_id() == id_;
781  }
782 
783  std::string attack::unit_info::dump()
784  {
785  std::stringstream s;
786  s << get_unit().type_id() << " (" << loc_.x + 1 << ',' << loc_.y + 1 << ')';
787  return s.str();
788  }
789 
790 
791  attack::attack(const map_location &attacker, const map_location &defender,
792  int attack_with, int defend_with, bool update_display) :
793  bc_(nullptr),
794  a_stats_(nullptr),
795  d_stats_(nullptr),
796  abs_n_attack_(0),
797  abs_n_defend_(0),
798  update_att_fog_(false),
799  update_def_fog_(false),
800  update_minimap_(false),
801  a_(attacker, attack_with, *resources::units),
802  d_(defender, defend_with, *resources::units),
803  units_(*resources::units),
804  errbuf_(),
805  update_display_(update_display),
806  OOS_error_(false)
807  {
808  }
809 
810  attack::~attack()
811  {
812  delete bc_;
813  }
814 
815  void attack::fire_event(const std::string& n)
816  {
817  LOG_NG << "firing " << n << " event\n";
818  //prepare the event data for weapon filtering
819  config ev_data;
820  config& a_weapon_cfg = ev_data.add_child("first");
821  config& d_weapon_cfg = ev_data.add_child("second");
822  if(a_stats_->weapon != nullptr && a_.valid()) {
823  a_stats_->weapon->write(a_weapon_cfg);
824  }
825  if(d_stats_->weapon != nullptr && d_.valid()) {
826  d_stats_->weapon->write(d_weapon_cfg);
827  }
828  if(a_weapon_cfg["name"].empty()) {
829  a_weapon_cfg["name"] = "none";
830  }
831  if(d_weapon_cfg["name"].empty()) {
832  d_weapon_cfg["name"] = "none";
833  }
834  if(n == "attack_end") {
835  // We want to fire attack_end event in any case! Even if one of units was removed by WML
836  resources::game_events->pump().fire(n, a_.loc_, d_.loc_, ev_data);
837  return;
838  }
839  const int defender_side = d_.get_unit().side();
841  game_events::entity_location(d_.loc_, d_.id_), ev_data);
842 
843  // The event could have killed either the attacker or
844  // defender, so we have to make sure they still exist
845  refresh_bc();
846  if(!a_.valid() || !d_.valid() || !(*resources::teams)[a_.get_unit().side() - 1].is_enemy(d_.get_unit().side())) {
847  actions::recalculate_fog(defender_side);
848  if (update_display_){
850  resources::screen->draw(true, true);
851  }
852  fire_event("attack_end");
853  throw attack_end_exception();
854  }
855  }
856 
857  void attack::refresh_bc()
858  {
859  // Fix index of weapons
860  if (a_.valid()) {
861  refresh_weapon_index(a_.weapon_, a_.weap_id_, a_.get_unit().attacks());
862  }
863  if (d_.valid()) {
864  refresh_weapon_index(d_.weapon_, d_.weap_id_, d_.get_unit().attacks());
865  }
866  if(!a_.valid() || !d_.valid()) {
867  // Fix pointer to weapons
868  const_cast<battle_context_unit_stats*>(a_stats_)->weapon =
869  a_.valid() && a_.weapon_ >= 0
870  ? &a_.get_unit().attacks()[a_.weapon_] : nullptr;
871 
872  const_cast<battle_context_unit_stats*>(d_stats_)->weapon =
873  d_.valid() && d_.weapon_ >= 0
874  ? &d_.get_unit().attacks()[d_.weapon_] : nullptr;
875 
876  return;
877  }
878 
879  *bc_ = battle_context(units_, a_.loc_, d_.loc_, a_.weapon_, d_.weapon_);
880  a_stats_ = &bc_->get_attacker_stats();
881  d_stats_ = &bc_->get_defender_stats();
882  a_.cth_ = a_stats_->chance_to_hit;
883  d_.cth_ = d_stats_->chance_to_hit;
884  a_.damage_ = a_stats_->damage;
885  d_.damage_ = d_stats_->damage;
886  }
887 
888  bool attack::perform_hit(bool attacker_turn, statistics::attack_context &stats)
889  {
890  unit_info
891  &attacker = *(attacker_turn ? &a_ : &d_),
892  &defender = *(attacker_turn ? &d_ : &a_);
894  *&attacker_stats = *(attacker_turn ? &a_stats_ : &d_stats_),
895  *&defender_stats = *(attacker_turn ? &d_stats_ : &a_stats_);
896  int &abs_n = *(attacker_turn ? &abs_n_attack_ : &abs_n_defend_);
897  bool &update_fog = *(attacker_turn ? &update_def_fog_ : &update_att_fog_);
898 
899  int ran_num = random_new::generator->get_random_int(0,99);
900  bool hits = (ran_num < attacker.cth_);
901 
902  int damage = 0;
903  if (hits) {
904  damage = attacker.damage_;
905  resources::gamedata->get_variable("damage_inflicted") = damage;
906  }
907 
908  // Make sure that if we're serializing a game here,
909  // we got the same results as the game did originally.
910  const config local_results = config_of("chance", attacker.cth_)("hits", hits)("damage", damage);
911  config replay_results;
912  bool equals_replay = checkup_instance->local_checkup(local_results, replay_results);
913  if (!equals_replay)
914  {
915  check_replay_attack_result(hits, ran_num, damage, replay_results, attacker);
916  }
917 
918  // can do no more damage than the defender has hitpoints
919  int damage_done = std::min<int>(defender.get_unit().hitpoints(), attacker.damage_);
920  // expected damage = damage potential * chance to hit (as a percentage)
921  double expected_damage = damage_done*attacker.cth_*0.01;
922  if (attacker_turn) {
923  stats.attack_expected_damage(expected_damage, 0);
924  } else {
925  stats.attack_expected_damage(0, expected_damage);
926  }
927 
928  int drains_damage = 0;
929  if (hits && attacker_stats->drains) {
930  drains_damage = damage_done * attacker_stats->drain_percent / 100 + attacker_stats->drain_constant;
931  // don't drain so much that the attacker gets more than his maximum hitpoints
932  drains_damage = std::min<int>(drains_damage, attacker.get_unit().max_hitpoints() - attacker.get_unit().hitpoints());
933  // if drain is negative, don't allow drain to kill the attacker
934  drains_damage = std::max<int>(drains_damage, 1 - attacker.get_unit().hitpoints());
935  }
936 
937  if (update_display_)
938  {
939  std::ostringstream float_text;
940  std::vector<std::string> extra_hit_sounds;
941  if (hits)
942  {
943  const unit &defender_unit = defender.get_unit();
944  if (attacker_stats->poisons && !defender_unit.get_state(unit::STATE_POISONED)) {
945  float_text << (defender_unit.gender() == unit_race::FEMALE ?
946  _("female^poisoned") : _("poisoned")) << '\n';
947 
948  extra_hit_sounds.push_back(game_config::sounds::status::poisoned);
949  }
950 
951  if (attacker_stats->slows && !defender_unit.get_state(unit::STATE_SLOWED)) {
952  float_text << (defender_unit.gender() == unit_race::FEMALE ?
953  _("female^slowed") : _("slowed")) << '\n';
954 
955  extra_hit_sounds.push_back(game_config::sounds::status::slowed);
956  }
957 
958  if (attacker_stats->petrifies) {
959  float_text << (defender_unit.gender() == unit_race::FEMALE ?
960  _("female^petrified") : _("petrified")) << '\n';
961 
962  extra_hit_sounds.push_back(game_config::sounds::status::petrified);
963  }
964  }
965 
967  attacker.loc_, defender.loc_, damage,
968  *attacker_stats->weapon, defender_stats->weapon,
969  abs_n, float_text.str(), drains_damage, "", &extra_hit_sounds);
970  }
971 
972  bool dies = defender.get_unit().take_hit(damage);
973  LOG_NG << "defender took " << damage << (dies ? " and died\n" : "\n");
974  if (attacker_turn) {
975  stats.attack_result(hits
977  : statistics::attack_context::MISSES, damage_done, drains_damage);
978  } else {
979  stats.defend_result(hits
981  : statistics::attack_context::MISSES, damage_done, drains_damage);
982  }
983 
984 
985  replay_results.clear();
986  // there was also a attribute cfg["unit_hit"] which was never used so i deleted.
987  equals_replay = checkup_instance->local_checkup(config_of("dies", dies), replay_results);
988  if (!equals_replay)
989  {
990  bool results_dies = replay_results["dies"].to_bool();
991 
992  errbuf_ << "SYNC: In attack " << a_.dump() << " vs " << d_.dump()
993  << ": the data source says the "
994  << (attacker_turn ? "defender" : "attacker") << ' '
995  << (results_dies ? "perished" : "survived")
996  << " while in-game calculations show it "
997  << (dies ? "perished" : "survived")
998  << " (over-riding game calculations with data source results)\n";
999  dies = results_dies;
1000  // Set hitpoints to 0 so later checks don't invalidate the death.
1001  if (results_dies) defender.get_unit().set_hitpoints(0);
1002  OOS_error_ = true;
1003  }
1004 
1005  if (hits)
1006  {
1007  try {
1008  fire_event(attacker_turn ? "attacker_hits" : "defender_hits");
1009  } catch (attack_end_exception) {
1010  refresh_bc();
1011  return false;
1012  }
1013  }
1014  else
1015  {
1016  try {
1017  fire_event(attacker_turn ? "attacker_misses" : "defender_misses");
1018  } catch (attack_end_exception) {
1019  refresh_bc();
1020  return false;
1021  }
1022  }
1023  refresh_bc();
1024 
1025  bool attacker_dies = false;
1026  if (drains_damage > 0) {
1027  attacker.get_unit().heal(drains_damage);
1028  } else if(drains_damage < 0) {
1029  attacker_dies = attacker.get_unit().take_hit(-drains_damage);
1030  }
1031 
1032  if (dies) {
1033  unit_killed(attacker, defender, attacker_stats, defender_stats, false);
1034  update_fog = true;
1035  }
1036  if (attacker_dies) {
1037  unit_killed(defender, attacker, defender_stats, attacker_stats, true);
1038  *(attacker_turn ? &update_att_fog_ : &update_def_fog_) = true;
1039  }
1040 
1041  if(dies) {
1042  update_minimap_ = true;
1043  return false;
1044  }
1045 
1046  if (hits)
1047  {
1048  unit &defender_unit = defender.get_unit();
1049  if (attacker_stats->poisons && !defender_unit.get_state(unit::STATE_POISONED)) {
1050  defender_unit.set_state(unit::STATE_POISONED, true);
1051  LOG_NG << "defender poisoned\n";
1052  }
1053 
1054  if (attacker_stats->slows && !defender_unit.get_state(unit::STATE_SLOWED)) {
1055  defender_unit.set_state(unit::STATE_SLOWED, true);
1056  update_fog = true;
1057  defender.damage_ = defender_stats->slow_damage;
1058  LOG_NG << "defender slowed\n";
1059  }
1060 
1061  // If the defender is petrified, the fight stops immediately
1062  if (attacker_stats->petrifies) {
1063  defender_unit.set_state(unit::STATE_PETRIFIED, true);
1064  update_fog = true;
1065  attacker.n_attacks_ = 0;
1066  defender.n_attacks_ = -1; // Petrified.
1067  resources::game_events->pump().fire("petrified", defender.loc_, attacker.loc_);
1068  refresh_bc();
1069  }
1070  }
1071 
1072  // Delay until here so that poison and slow go through
1073  if(attacker_dies) {
1074  update_minimap_ = true;
1075  return false;
1076  }
1077 
1078  --attacker.n_attacks_;
1079  return true;
1080  }
1081 
1082  void attack::unit_killed(unit_info& attacker, unit_info& defender,
1083  const battle_context_unit_stats *&attacker_stats,
1084  const battle_context_unit_stats *&defender_stats,
1085  bool drain_killed)
1086  {
1087  attacker.xp_ = game_config::kill_xp(defender.get_unit().level());
1088  defender.xp_ = 0;
1089  resources::screen->invalidate(attacker.loc_);
1090 
1091  game_events::entity_location death_loc(defender.loc_, defender.id_);
1092  game_events::entity_location attacker_loc(attacker.loc_, attacker.id_);
1093  std::string undead_variation = defender.get_unit().undead_variation();
1094  fire_event("attack_end");
1095  refresh_bc();
1096 
1097  // get weapon info for last_breath and die events
1098  config dat;
1099  config a_weapon_cfg = attacker_stats->weapon && attacker.valid() ?
1100  attacker_stats->weapon->to_config() : config();
1101  config d_weapon_cfg = defender_stats->weapon && defender.valid() ?
1102  defender_stats->weapon->to_config() : config();
1103  if (a_weapon_cfg["name"].empty())
1104  a_weapon_cfg["name"] = "none";
1105  if (d_weapon_cfg["name"].empty())
1106  d_weapon_cfg["name"] = "none";
1107  dat.add_child("first", d_weapon_cfg);
1108  dat.add_child("second", a_weapon_cfg);
1109 
1110  resources::game_events->pump().fire("last breath", death_loc, attacker_loc, dat);
1111  refresh_bc();
1112 
1113  if (!defender.valid() || defender.get_unit().hitpoints() > 0) {
1114  // WML has invalidated the dying unit, abort
1115  return;
1116  }
1117 
1118  if (!attacker.valid()) {
1119  unit_display::unit_die(defender.loc_, defender.get_unit(),
1120  nullptr, defender_stats->weapon);
1121  } else {
1122  unit_display::unit_die(defender.loc_, defender.get_unit(),
1123  attacker_stats->weapon, defender_stats->weapon,
1124  attacker.loc_, &attacker.get_unit());
1125  }
1126 
1127  resources::game_events->pump().fire("die", death_loc, attacker_loc, dat);
1128  refresh_bc();
1129 
1130  if (!defender.valid() || defender.get_unit().hitpoints() > 0) {
1131  // WML has invalidated the dying unit, abort
1132  return;
1133  }
1134 
1135  units_.erase(defender.loc_);
1136 
1137  if (attacker.valid() && attacker_stats->plagues && !drain_killed)
1138  {
1139  // plague units make new units on the target hex
1140  LOG_NG << "trying to reanimate " << attacker_stats->plague_type << '\n';
1141  const unit_type *reanimator =
1142  unit_types.find(attacker_stats->plague_type);
1143  if (reanimator)
1144  {
1145  LOG_NG << "found unit type:" << reanimator->id() << '\n';
1146  unit newunit(*reanimator, attacker.get_unit().side(),
1147  true, unit_race::MALE);
1148  newunit.set_attacks(0);
1149  newunit.set_movement(0, true);
1150  // Apply variation
1151  if (undead_variation != "null")
1152  {
1153  config mod;
1154  config &variation = mod.add_child("effect");
1155  variation["apply_to"] = "variation";
1156  variation["name"] = undead_variation;
1157  newunit.add_modification("variation",mod);
1158  newunit.heal_all();
1159  }
1160  units_.add(death_loc, newunit);
1161 
1162  game_events::entity_location reanim_loc(defender.loc_, newunit.underlying_id());
1163  resources::game_events->pump().fire("unit placed", reanim_loc);
1164 
1165  preferences::encountered_units().insert(newunit.type_id());
1166  if (update_display_) {
1167  resources::screen->invalidate(death_loc);
1168  }
1169  }
1170  }
1171  else
1172  {
1173  LOG_NG << "unit not reanimated\n";
1174  }
1175  }
1176 
1177  void attack::perform()
1178  {
1179  // Stop the user from issuing any commands while the units are fighting
1180  const events::command_disabler disable_commands;
1181 
1182  if(!a_.valid() || !d_.valid()) {
1183  return;
1184  }
1185 
1186  // no attack weapon => stop here and don't attack
1187  if (a_.weapon_ < 0) {
1188  a_.get_unit().set_attacks(a_.get_unit().attacks_left()-1);
1189  a_.get_unit().set_movement(-1, true);
1190  return;
1191  }
1192 
1193  a_.get_unit().set_facing(a_.loc_.get_relative_dir(d_.loc_));
1194  d_.get_unit().set_facing(d_.loc_.get_relative_dir(a_.loc_));
1195 
1196  a_.get_unit().set_attacks(a_.get_unit().attacks_left()-1);
1197  VALIDATE(a_.weapon_ < static_cast<int>(a_.get_unit().attacks().size()),
1198  _("An invalid attacker weapon got selected."));
1199  a_.get_unit().set_movement(a_.get_unit().movement_left() -
1200  a_.get_unit().attacks()[a_.weapon_].movement_used(), true);
1201  a_.get_unit().set_state(unit::STATE_NOT_MOVED,false);
1202  a_.get_unit().set_resting(false);
1203  d_.get_unit().set_resting(false);
1204 
1205  // If the attacker was invisible, she isn't anymore!
1206  a_.get_unit().set_state(unit::STATE_UNCOVERED, true);
1207 
1208  bc_ = new battle_context(units_, a_.loc_, d_.loc_, a_.weapon_, d_.weapon_);
1209  a_stats_ = &bc_->get_attacker_stats();
1210  d_stats_ = &bc_->get_defender_stats();
1211  if(a_stats_->weapon) {
1212  a_.weap_id_ = a_stats_->weapon->id();
1213  }
1214  if(d_stats_->weapon) {
1215  d_.weap_id_ = d_stats_->weapon->id();
1216  }
1217 
1218  try {
1219  fire_event("attack");
1220  } catch (attack_end_exception) {
1221  return;
1222  }
1223  refresh_bc();
1224 
1225  DBG_NG << "getting attack statistics\n";
1226  statistics::attack_context attack_stats(a_.get_unit(), d_.get_unit(), a_stats_->chance_to_hit, d_stats_->chance_to_hit);
1227 
1228  a_.orig_attacks_ = a_stats_->num_blows;
1229  d_.orig_attacks_ = d_stats_->num_blows;
1230  a_.n_attacks_ = a_.orig_attacks_;
1231  d_.n_attacks_ = d_.orig_attacks_;
1232  a_.xp_ = d_.get_unit().level();
1233  d_.xp_ = a_.get_unit().level();
1234 
1235  bool defender_strikes_first = (d_stats_->firststrike && !a_stats_->firststrike);
1236  unsigned int rounds = std::max<unsigned int>(a_stats_->rounds, d_stats_->rounds) - 1;
1237  const int defender_side = d_.get_unit().side();
1238 
1239  LOG_NG << "Fight: (" << a_.loc_ << ") vs (" << d_.loc_ << ") ATT: " <<
1240  a_stats_->weapon->name() << " " << a_stats_->damage << "-" <<
1241  a_stats_->num_blows << "(" << a_stats_->chance_to_hit << "%) vs DEF: " <<
1242  (d_stats_->weapon ? d_stats_->weapon->name() : "none") << " " <<
1243  d_stats_->damage << "-" << d_stats_->num_blows <<
1244  "(" << d_stats_->chance_to_hit << "%)" <<
1245  (defender_strikes_first ? " defender first-strike" : "") << "\n";
1246 
1247  // Play the pre-fight animation
1248  unit_display::unit_draw_weapon(a_.loc_,a_.get_unit(),a_stats_->weapon,d_stats_->weapon,d_.loc_,&d_.get_unit());
1249 
1250  for (;;)
1251  {
1252  DBG_NG << "start of attack loop...\n";
1253  ++abs_n_attack_;
1254 
1255  if (a_.n_attacks_ > 0 && !defender_strikes_first) {
1256  if (!perform_hit(true, attack_stats)) {
1257  DBG_NG << "broke from attack loop on attacker turn\n";
1258  break;
1259  }
1260  }
1261 
1262  // If the defender got to strike first, they use it up here.
1263  defender_strikes_first = false;
1264  ++abs_n_defend_;
1265 
1266  if (d_.n_attacks_ > 0) {
1267  if (!perform_hit(false, attack_stats)) {
1268  DBG_NG << "broke from attack loop on defender turn\n";
1269  break;
1270  }
1271  }
1272 
1273  // Continue the fight to death; if one of the units got petrified,
1274  // either n_attacks or n_defends is -1
1275  if(rounds > 0 && d_.n_attacks_ == 0 && a_.n_attacks_ == 0) {
1276  a_.n_attacks_ = a_.orig_attacks_;
1277  d_.n_attacks_ = d_.orig_attacks_;
1278  --rounds;
1279  defender_strikes_first = (d_stats_->firststrike && ! a_stats_->firststrike);
1280  }
1281 
1282  if (a_.n_attacks_ <= 0 && d_.n_attacks_ <= 0) {
1283  fire_event("attack_end");
1284  refresh_bc();
1285  break;
1286  }
1287  }
1288 
1289  if ( update_def_fog_ )
1290  {
1291  actions::recalculate_fog(defender_side);
1292  }
1293 
1294  // TODO: if we knew the viewing team, we could skip this display update
1295  if (update_minimap_ && update_display_) {
1297  }
1298 
1299  if (a_.valid()) {
1300  unit &u = a_.get_unit();
1301  u.anim_comp().set_standing();
1302  u.set_experience(u.experience() + a_.xp_);
1303  }
1304 
1305  if (d_.valid()) {
1306  unit &u = d_.get_unit();
1307  u.anim_comp().set_standing();
1308  u.set_experience(u.experience() + d_.xp_);
1309  }
1310 
1311  unit_display::unit_sheath_weapon(a_.loc_,a_.valid()?&a_.get_unit():nullptr,a_stats_->weapon,
1312  d_stats_->weapon,d_.loc_,d_.valid()?&d_.get_unit():nullptr);
1313 
1314  if (update_display_){
1316  resources::screen->invalidate(a_.loc_);
1317  resources::screen->invalidate(d_.loc_);
1318  resources::screen->draw(true, true);
1319  }
1320 
1321  if(OOS_error_) {
1322  replay::process_error(errbuf_.str());
1323  }
1324  }
1325 
1326  void attack::check_replay_attack_result(bool hits, int ran_num, int damage,
1327  config replay_results, unit_info& attacker)
1328  {
1329  int results_chance = replay_results["chance"];
1330  bool results_hits = replay_results["hits"].to_bool();
1331  int results_damage = replay_results["damage"];
1332  /*
1333  errbuf_ << "SYNC: In attack " << a_.dump() << " vs " << d_.dump()
1334  << " replay data differs from local calculated data:"
1335  << " chance to hit in data source: " << results_chance
1336  << " chance to hit in calculated: " << attacker.cth_
1337  << " chance to hit in data source: " << results_chance
1338  << " chance to hit in calculated: " << attacker.cth_
1339  ;
1340 
1341  attacker.cth_ = results_chance;
1342  hits = results_hits;
1343  damage = results_damage;
1344 
1345  OOS_error_ = true;
1346  */
1347  if (results_chance != attacker.cth_)
1348  {
1349  errbuf_ << "SYNC: In attack " << a_.dump() << " vs " << d_.dump()
1350  << ": chance to hit is inconsistent. Data source: "
1351  << results_chance << "; Calculation: " << attacker.cth_
1352  << " (over-riding game calculations with data source results)\n";
1353  attacker.cth_ = results_chance;
1354  OOS_error_ = true;
1355  }
1356 
1357  if (results_hits != hits)
1358  {
1359  errbuf_ << "SYNC: In attack " << a_.dump() << " vs " << d_.dump()
1360  << ": the data source says the hit was "
1361  << (results_hits ? "successful" : "unsuccessful")
1362  << ", while in-game calculations say the hit was "
1363  << (hits ? "successful" : "unsuccessful")
1364  << " random number: " << ran_num << " = "
1365  << (ran_num % 100) << "/" << results_chance
1366  << " (over-riding game calculations with data source results)\n";
1367  hits = results_hits;
1368  OOS_error_ = true;
1369  }
1370 
1371  if (results_damage != damage)
1372  {
1373  errbuf_ << "SYNC: In attack " << a_.dump() << " vs " << d_.dump()
1374  << ": the data source says the hit did " << results_damage
1375  << " damage, while in-game calculations show the hit doing "
1376  << damage
1377  << " damage (over-riding game calculations with data source results)\n";
1378  damage = results_damage;
1379  OOS_error_ = true;
1380  }
1381  }
1382 } //end anonymous namespace
1383 
1384 void attack_unit(const map_location &attacker, const map_location &defender,
1385  int attack_with, int defend_with, bool update_display)
1386 {
1387  attack dummy(attacker, defender, attack_with, defend_with, update_display);
1388  dummy.perform();
1389 }
1390 
1391 
1392 namespace
1393 {
1394  class unit_advancement_choice : public mp_sync::user_choice
1395  {
1396  public:
1397  unit_advancement_choice(const map_location& loc, int total_opt, int side_num, const ai::unit_advancements_aspect* ai_advancement, bool force_dialog)
1398  : loc_ (loc), nb_options_(total_opt), side_num_(side_num), ai_advancement_(ai_advancement), force_dialog_(force_dialog)
1399  {
1400  }
1401 
1402  virtual ~unit_advancement_choice()
1403  {
1404  }
1405 
1406  virtual config query_user(int /*side*/) const
1407  {
1408  //the 'side' parameter might differ from side_num_-
1409  int res = 0;
1410  team t = (*resources::teams)[side_num_ - 1];
1411  //i wonder how this got included here ?
1412  bool is_mp = resources::controller->is_networked_mp();
1413  bool is_current_side = resources::controller->current_side() == side_num_;
1414  //note, that the advancements for networked sides are also determined on the current playing side.
1415 
1416  //to make mp games equal we only allow selecting advancements to the current side.
1417  //otherwise we'd give an unfair advantage to the side that hosts ai sides if units advance during ai turns.
1418  if(!CVideo::get_singleton().non_interactive() && (force_dialog_ || (t.is_local_human() && !t.is_idle() && (is_current_side || !is_mp))))
1419  {
1421  }
1422  else if(t.is_local_ai() || t.is_network_ai() || t.is_empty())
1423  {
1424  res = rand() % nb_options_;
1425 
1426  //if ai_advancement_ is the default advancement the following code will
1427  //have no effect because get_advancements returns an empty list.
1428  if(ai_advancement_ != nullptr)
1429  {
1431  const std::vector<std::string>& options = u->advances_to();
1432  const std::vector<std::string>& allowed = ai_advancement_->get_advancements(u);
1433 
1434  for(std::vector<std::string>::const_iterator a = options.begin(); a != options.end(); ++a) {
1435  if (std::find(allowed.begin(), allowed.end(), *a) != allowed.end()){
1436  res = a - options.begin();
1437  break;
1438  }
1439  }
1440  }
1441 
1442  }
1443  else
1444  {
1445  //we are in the situation, that the unit is owned by a human, but he's not allowed to do this decision.
1446  //because it's a mp game and it's not his turn.
1447  //note that it doesn't matter whether we call random_new::generator->next_random() or rand().
1448  res = random_new::generator->get_random_int(0, nb_options_-1);
1449  }
1450  LOG_NG << "unit at position " << loc_ << "choose advancement number " << res << "\n";
1451  config retv;
1452  retv["value"] = res;
1453  return retv;
1454 
1455  }
1456  virtual config random_choice(int /*side*/) const
1457  {
1458  config retv;
1459  retv["value"] = 0;
1460  return retv;
1461  }
1462  virtual std::string description() const
1463  {
1464  return "an advancement choice";
1465  }
1466  private:
1467  const map_location loc_;
1468  int nb_options_;
1469  int side_num_;
1470  const ai::unit_advancements_aspect* ai_advancement_;
1471  bool force_dialog_;
1472  };
1473 }
1474 
1475 /*
1476 advances the unit and stores data in the replay (or reads data from replay).
1477 */
1479 {
1480  //i just don't want infinite loops...
1481  // the 20 is picked rather randomly.
1482  for(int advacment_number = 0; advacment_number < 20; advacment_number++)
1483  {
1485  //this implies u.valid()
1487  return;
1488  }
1489 
1490  if(params.fire_events_)
1491  {
1492  LOG_NG << "Firing pre advance event at " << params.loc_ <<".\n";
1493  resources::game_events->pump().fire("pre advance", params.loc_);
1494  //TODO: maybe use id instead of location here ?.
1495  u = resources::units->find(params.loc_);
1497  {
1498  LOG_NG << "pre advance event aborted advancing.\n";
1499  return;
1500  }
1501  }
1502  //we don't want to let side 1 decide it during start/prestart.
1503  int side_for = resources::gamedata->phase() == game_data::PLAY ? 0: u->side();
1505  unit_advancement_choice(params.loc_, unit_helper::number_of_possible_advances(*u), u->side(), params.ai_advancements_, params.force_dialog_), side_for);
1506  //calls actions::advance_unit.
1507  bool result = dialogs::animate_unit_advancement(params.loc_, selected["value"], params.fire_events_, params.animate_);
1508 
1509  DBG_NG << "animate_unit_advancement result = " << result << std::endl;
1510  u = resources::units->find(params.loc_);
1511  // level 10 unit gives 80 XP and the highest mainline is level 5
1512  if (u.valid() && u->experience() > 80)
1513  {
1514  WRN_NG << "Unit has too many (" << u->experience() << ") XP left; cascade leveling goes on still." << std::endl;
1515  }
1516  }
1517  ERR_NG << "unit at " << params.loc_ << "tried to advance more than 20 times. Advancing was aborted" << std::endl;
1518 }
1519 
1520 
1522 { advance_unit_at(advance_unit_params(loc).ai_advancements(ai_advancement)); }
1523 
1524 void attack_unit_and_advance(const map_location &attacker, const map_location &defender,
1525  int attack_with, int defend_with, bool update_display,
1526  const ai::unit_advancements_aspect& ai_advancement)
1527 {
1528  attack_unit(attacker, defender, attack_with, defend_with, update_display);
1530  if (atku != resources::units->end()) {
1531  advance_unit_at(attacker, ai_advancement);
1532  }
1533 
1535  if (defu != resources::units->end()) {
1536  advance_unit_at(defender, ai_advancement);
1537  }
1538 }
1539 
1540 
1541 unit_ptr get_advanced_unit(const unit &u, const std::string& advance_to)
1542 {
1543  const unit_type *new_type = unit_types.find(advance_to);
1544  if (!new_type) {
1545  throw game::game_error("Could not find the unit being advanced"
1546  " to: " + advance_to);
1547  }
1548  unit_ptr new_unit(new unit(u));
1549  new_unit->set_experience(new_unit->experience() - new_unit->max_experience());
1550  new_unit->advance_to(*new_type);
1551  new_unit->heal_all();
1552  new_unit->set_state(unit::STATE_POISONED, false);
1553  new_unit->set_state(unit::STATE_SLOWED, false);
1554  new_unit->set_state(unit::STATE_PETRIFIED, false);
1555  new_unit->set_user_end_turn(false);
1556  new_unit->set_hidden(false);
1557  return new_unit;
1558 }
1559 
1560 
1561 /**
1562  * Returns the AMLA-advanced version of a unit (with traits and items retained).
1563  */
1564 unit_ptr get_amla_unit(const unit &u, const config &mod_option)
1565 {
1566  unit_ptr amla_unit(new unit(u));
1567  amla_unit->set_experience(amla_unit->experience() - amla_unit->max_experience());
1568  amla_unit->add_modification("advancement", mod_option);
1569  return amla_unit;
1570 }
1571 
1572 
1573 void advance_unit(map_location loc, const std::string &advance_to,
1574  const bool &fire_event, const config * mod_option)
1575 {
1577  if(!u.valid()) {
1578  return;
1579  }
1580  // original_type is not a reference, since the unit may disappear at any moment.
1581  std::string original_type = u->type_id();
1582 
1583  // "advance" event.
1584  if(fire_event)
1585  {
1586  LOG_NG << "Firing advance event at " << loc <<".\n";
1587  resources::game_events->pump().fire("advance",loc);
1588 
1589  if (!u.valid() || u->experience() < u->max_experience() ||
1590  u->type_id() != original_type)
1591  {
1592  LOG_NG << "WML has invalidated the advancing unit. Aborting.\n";
1593  return;
1594  }
1595  // In case WML moved the unit:
1596  loc = u->get_location();
1597  }
1598 
1599  // This is not normally necessary, but if a unit loses power when leveling
1600  // (e.g. loses "jamming" or ambush), it could be discovered as a result of
1601  // the advancement.
1602  std::vector<int> not_seeing = actions::get_sides_not_seeing(*u);
1603 
1604  // Create the advanced unit.
1605  bool use_amla = mod_option != nullptr;
1606  unit_ptr new_unit = use_amla ? get_amla_unit(*u, *mod_option) :
1607  get_advanced_unit(*u, advance_to);
1608  if ( !use_amla )
1609  {
1610  statistics::advance_unit(*new_unit);
1611  preferences::encountered_units().insert(new_unit->type_id());
1612  LOG_CF << "Added '" << new_unit->type_id() << "' to the encountered units.\n";
1613  }
1614  u = resources::units->replace(loc, *new_unit).first;
1615 
1616  // Update fog/shroud.
1617  actions::shroud_clearer clearer;
1618  clearer.clear_unit(loc, *new_unit);
1619 
1620  // "post_advance" event.
1621  if(fire_event)
1622  {
1623  LOG_NG << "Firing post_advance event at " << loc << ".\n";
1624  resources::game_events->pump().fire("post advance",loc);
1625  }
1626 
1627  // "sighted" event(s).
1628  clearer.fire_events();
1629  if ( u.valid() )
1630  actions::actor_sighted(*u, &not_seeing);
1631 
1632  resources::whiteboard->on_gamestate_change();
1633 }
1634 
1636  int* bonus)
1637 {
1638  const unit_map::const_iterator un = units.find(loc);
1639  if(un == units.end()) {
1640  return map_location::null_location();
1641  }
1642  unit_ability_list abil = un->get_abilities("leadership");
1643  if(bonus) {
1644  *bonus = abil.highest("value").first;
1645  }
1646  return abil.highest("value").second;
1647 }
1648 
1649 int combat_modifier(const unit_map & units, const gamemap & map, const map_location &loc, unit_type::ALIGNMENT alignment,
1650  bool is_fearless)
1651 {
1652  const tod_manager & tod_m = *resources::tod_manager;
1653  int lawful_bonus = tod_m.get_illuminated_time_of_day(units, map, loc).lawful_bonus;
1654  return generic_combat_modifier(lawful_bonus, alignment, is_fearless);
1655 }
1656 
1657 int generic_combat_modifier(int lawful_bonus, unit_type::ALIGNMENT alignment,
1658  bool is_fearless) {
1659  int bonus;
1660  switch(alignment.v) {
1661  case unit_type::ALIGNMENT::LAWFUL:
1662  bonus = lawful_bonus;
1663  break;
1664  case unit_type::ALIGNMENT::NEUTRAL:
1665  bonus = 0;
1666  break;
1667  case unit_type::ALIGNMENT::CHAOTIC:
1668  bonus = -lawful_bonus;
1669  break;
1670  case unit_type::ALIGNMENT::LIMINAL:
1671  bonus = -abs(lawful_bonus);
1672  break;
1673  default:
1674  bonus = 0;
1675  }
1676 
1677  if(is_fearless)
1678  bonus = std::max<int>(bonus, 0);
1679 
1680  return bonus;
1681 }
1682 
1683 
1684 bool backstab_check(const map_location& attacker_loc,
1685  const map_location& defender_loc,
1686  const unit_map& units, const std::vector<team>& teams)
1687 {
1688  const unit_map::const_iterator defender = units.find(defender_loc);
1689  if(defender == units.end()) return false; // No defender
1690 
1691  map_location adj[6];
1692  get_adjacent_tiles(defender_loc, adj);
1693  int i;
1694  for(i = 0; i != 6; ++i) {
1695  if(adj[i] == attacker_loc)
1696  break;
1697  }
1698  if(i >= 6) return false; // Attack not from adjacent location
1699 
1700  const unit_map::const_iterator opp =
1701  units.find(adj[(i+3)%6]);
1702  if(opp == units.end()) return false; // No opposite unit
1703  if (opp->incapacitated()) return false;
1704  if (size_t(defender->side() - 1) >= teams.size() || size_t(opp->side() - 1) >= teams.size())
1705  return true; // If sides aren't valid teams, then they are enemies
1706  if (teams[defender->side() - 1].is_enemy(opp->side()))
1707  return true; // Defender and opposite are enemies
1708  return false; // Defender and opposite are friends
1709 }
1710 
int kill_xp(int level)
Definition: game_config.hpp:45
void advance_unit(map_location loc, const std::string &advance_to, const bool &fire_event, const config *mod_option)
Function which will advance the unit at loc to 'advance_to'.
Definition: attack.cpp:1573
play_controller * controller
Definition: resources.cpp:21
bool is_local_ai() const
Definition: team.hpp:268
virtual std::string description() const
config get_user_choice(const std::string &name, const user_choice &uch, int side=0)
unsigned int calc_blows(unsigned new_hp) const
Calculates the number of blows we would have if we had new_hp.
Definition: attack.hpp:103
#define LOG_NG
Definition: attack.cpp:58
::tod_manager * tod_manager
Definition: resources.cpp:31
PHASE phase() const
Definition: game_data.hpp:78
unit_iterator end()
Definition: map.hpp:311
std::vector< double > hp_dist
Resulting probability distribution (might be not as large as max_hp)
const ai::unit_advancements_aspect * ai_advancements_
Definition: attack.hpp:224
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:3536
int max_hitpoints() const
Definition: unit.hpp:169
bool get_state(const std::string &state) const
Definition: unit.cpp:1289
std::string id_
Definition: formula.cpp:636
std::string plague_type
The plague type used by the attack, if any.
Definition: attack.hpp:83
static lg::log_domain log_config("config")
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
bool clear_unit(const map_location &view_loc, team &view_team, size_t viewer_id, int sight_range, bool slowed, const movetype::terrain_costs &costs, const map_location &real_loc, const std::set< map_location > *known_units=nullptr, size_t *enemy_count=nullptr, size_t *friend_count=nullptr, move_unit_spectator *spectator=nullptr, bool instant=true)
Clears shroud (and fog) around the provided location for view_team based on sight_range, costs, and slowed.
Definition: vision.cpp:331
GLint level
Definition: glew.h:1220
int experience() const
Definition: unit.hpp:171
unsigned int hp
Hitpoints of the unit at the beginning of the battle.
Definition: attack.hpp:72
Various functions implementing vision (through fog of war and shroud).
void unit_attack(display *disp, game_board &board, const map_location &a, const map_location &b, int damage, const attack_type &attack, const attack_type *secondary_attack, int swing, std::string hit_text, int drain_amount, std::string att_text, const std::vector< std::string > *extra_hit_sounds)
Make the unit on tile 'a' attack the unit on tile 'b'.
Definition: udisplay.cpp:572
double attack_weight() const
Definition: attack_type.hpp:47
int choose_defender_weapon(const unit &attacker, const unit &defender, unsigned attacker_weapon, const unit_map &units, const map_location &attacker_loc, const map_location &defender_loc, const combatant *prev_def)
Definition: attack.cpp:569
int modified_damage(bool is_backstab) const
Returns the damage per attack of this weapon, considering specials.
Definition: abilities.cpp:743
Various functions that implement attacks and attack calculations.
int max_range() const
Definition: attack_type.hpp:41
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
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:442
bool musthave_status(const std::string &status) const
Definition: types.cpp:669
rng * generator
This generator is automatically synced during synced context.
Definition: random_new.cpp:52
int hitpoints() const
Definition: unit.hpp:168
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition: attack.cpp:410
void advance_unit_at(const advance_unit_params &params)
Definition: attack.cpp:1478
bool will_certainly_advance(const unit_map::iterator &u)
Encapsulates the logic for deciding whether an iterator u points to a unit that can advance...
Definition: helper.cpp:31
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
game_display * screen
Definition: resources.cpp:27
#define ERR_NG
Definition: attack.cpp:60
void invalidate_unit()
Function to invalidate that unit status displayed on the sidebar.
bool actor_sighted(const unit &target, const std::vector< int > *cache)
Fires sighted events for the sides that can see target.
Definition: vision.cpp:623
const std::string & undead_variation() const
Info on the type of unit that the unit reanimates as.
Definition: types.hpp:107
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:70
unit_ptr get_advanced_unit(const unit &u, const std::string &advance_to)
Returns the advanced version of a unit (with traits and items retained).
Definition: attack.cpp:1541
unit_type_data unit_types
Definition: types.cpp:1314
bool fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
Definition: pump.cpp:471
bool is_slowed
True if the unit is slowed at the beginning of the battle.
Definition: attack.hpp:55
static CVideo & get_singleton()
Definition: video.hpp:75
battle_context_unit_stats * attacker_stats_
Statistics of the units.
Definition: attack.hpp:190
Replay control code.
const attack_type * weapon
The weapon used by the unit to attack the opponent, or nullptr if there is none.
Definition: attack.hpp:51
std::pair< int, map_location > highest(const std::string &key, int def=0) const
Definition: abilities.cpp:426
const GLfloat * params
Definition: glew.h:1499
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, const unit *attacker_ptr=nullptr)
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:320
bool slows
Attack slows opponent when it hits.
Definition: attack.hpp:56
void clear()
Definition: config.cpp:1055
void defend_result(hit_result res, int damage, int drain)
Definition: statistics.cpp:456
virtual void draw()
Draws invalidated items.
Definition: display.cpp:2706
void set_state(const std::string &state, bool value)
Definition: unit.cpp:1337
int drain_constant
Base HP drained regardless of damage dealt.
Definition: attack.hpp:78
-file sdl_utils.hpp
int level() const
Definition: types.hpp:126
const map_location & loc_
Definition: attack.hpp:223
int parry() const
Definition: attack_type.hpp:44
int accuracy() const
Definition: attack_type.hpp:43
unit_ability & front()
Definition: unit.hpp:81
GLdouble GLdouble t
Definition: glew.h:1366
int advance_unit_dialog(const map_location &loc)
Lets the user to select a unit advancement.
Definition: dialogs.cpp:172
void redraw_minimap()
Schedule the minimap to be redrawn.
Definition: display.hpp:629
unsigned int chance_to_hit
Effective chance to hit as a percentage (all factors accounted for).
Definition: attack.hpp:74
virtual config random_choice(int side) const =0
int defense_modifier(const t_translation::t_terrain &terrain) const
Definition: unit.cpp:1520
game_data * gamedata
Definition: resources.cpp:22
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:70
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
map_location loc_
const unit_type & type() const
The type of the unit (accounting for gender and variation).
Definition: unit.hpp:144
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
bool poisons
Attack poisons opponent when it hits.
Definition: attack.hpp:60
int get_random_int(int min, int max)
This helper method provides a random int from the underlying generator, using results of next_random...
Definition: random_new.hpp:50
const combatant & get_defender_combatant(const combatant *prev_def=nullptr)
Definition: attack.cpp:422
static std::vector< team > *& teams
Definition: team.cpp:50
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:61
const config & options()
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:50
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
int damage
Effective damage of the weapon (all factors accounted for).
Definition: attack.hpp:75
GLuint GLuint end
Definition: glew.h:1221
GLuint64EXT * result
Definition: glew.h:10727
int current_side() const
Returns the number of the side whose turn it is.
std::vector< team > * teams
Definition: resources.cpp:29
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
const std::string & undead_variation() const
Definition: unit.hpp:154
combatant * defender_combatant_
Definition: attack.hpp:193
unit_type::ALIGNMENT alignment() const
Definition: unit.hpp:368
bool valid() const
Definition: location.hpp:69
bool animate_unit_advancement(const map_location &loc, size_t choice, const bool &fire_event, const bool animate)
Actually levels a unit up.
Definition: dialogs.cpp:233
unsigned int rounds
Berserk special can force us to fight more than one round.
Definition: attack.hpp:71
unsigned int swarm_min
Minimum number of blows with swarm (equal to num_blows if swarm isn't used).
Definition: attack.hpp:80
size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.hpp:357
unit_ptr get_amla_unit(const unit &u, const config &mod_option)
Returns the AMLA-advanced version of a unit (with traits and items retained).
Definition: attack.cpp:1564
game_board * gameboard
Definition: resources.cpp:20
Interface for querying local choices.
std::string selected
Definition: game_config.cpp:84
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
bool plagues
Attack turns opponent into a zombie when fatal.
Definition: attack.hpp:59
bool better_attack(class battle_context &that, double harm_weight)
Given this harm_weight, is this attack better than that?
Definition: attack.cpp:435
Encapsulates the map of the game.
Definition: map.hpp:37
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:705
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:135
config & add_child(const std::string &key)
Definition: config.cpp:743
config to_config() const
Definition: attack_type.hpp:94
void fight(combatant &opponent, bool levelup_considered=true)
Simulate a fight! Can be called multiple times for cumulative calculations.
unit_race::GENDER gender() const
Definition: unit.hpp:203
bool is_local_human() const
Definition: team.hpp:267
checkup * checkup_instance
int attack_num
Index into unit->attacks() or -1 for none.
Definition: attack.hpp:52
Structure describing the statistics of a unit involved in the battle.
Definition: attack.hpp:49
unit_animation_component & anim_comp() const
Definition: unit.hpp:276
int damage_from(const attack_type &attack, bool attacker, const map_location &loc) const
Definition: unit.hpp:274
static const map_location & null_location()
Definition: location.hpp:195
std::pair< unit_iterator, bool > replace(const map_location &l, const unit &u)
Works like unit_map::add; but l is emptied first, if needed.
Definition: map.cpp:207
Error used for any general game error, e.g.
Definition: game_errors.hpp:46
int choose_attacker_weapon(const unit &attacker, const unit &defender, const unit_map &units, const map_location &attacker_loc, const map_location &defender_loc, double harm_weight, int *defender_weapon, const combatant *prev_def)
Definition: attack.cpp:472
#define WRN_NG
Definition: attack.cpp:59
game_events::manager * game_events
Definition: resources.cpp:24
int generic_combat_modifier(int lawful_bonus, unit_type::ALIGNMENT alignment, bool is_fearless)
Returns the amount that a unit's damage should be multiplied by due to a given lawful_bonus.
Definition: attack.cpp:1657
virtual config query_user(int side) const =0
const std::string & type() const
Definition: attack_type.hpp:37
const std::vector< attack_type > & attacks() const
Definition: unit.hpp:271
void unit_draw_weapon(const map_location &loc, unit &attacker, const attack_type *attack, const attack_type *secondary_attack, const map_location &defender_loc, unit *defender)
Play a pre-fight animation First unit is the attacker, second unit the defender.
Definition: udisplay.cpp:500
advances the unit at loc if it has enough experience, maximum 20 times.
Definition: attack.hpp:214
Encapsulates the map of the game.
Definition: location.hpp:38
battle_context_unit_stats(const unit &u, const map_location &u_loc, int u_attack_num, bool attacking, const unit &opp, const map_location &opp_loc, const attack_type *opp_weapon, const unit_map &units)
Definition: attack.cpp:68
void unit_die(const map_location &loc, unit &loser, const attack_type *attack, const attack_type *secondary_attack, const map_location &winner_loc, unit *winner)
Show a unit fading out.
Definition: udisplay.cpp:548
static void process_error(const std::string &msg)
Definition: replay.cpp:198
bool empty() const
Definition: unit.hpp:80
All combat-related info.
void advance_unit(const unit &u)
Definition: statistics.cpp:516
GLuint res
Definition: glew.h:9258
battle_context & operator=(const battle_context &other)
Definition: attack.cpp:393
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: util.hpp:60
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:1684
bool swarm
Attack has swarm special.
Definition: attack.hpp:65
int slow_damage
Effective damage if unit becomes slowed (== damage, if already slowed)
Definition: attack.hpp:76
#define LOG_CF
Definition: attack.cpp:63
combatant * attacker_combatant_
Outcome of simulated fight.
Definition: attack.hpp:193
const std::string & range() const
Definition: attack_type.hpp:39
Define the game's event mechanism.
bool disable
Attack has disable special.
Definition: attack.hpp:67
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
bool firststrike
Attack has firststrike special.
Definition: attack.hpp:66
int number_of_possible_advances(const unit &u)
Determines the total number of available advancements (of any kind) for a given unit.
Definition: helper.cpp:26
std::set< std::string > & encountered_units()
variant a_
Definition: function.cpp:808
bool fire_event(const tevent event, std::vector< std::pair< twidget *, tevent > > &event_chain, twidget *dispatcher, twidget *widget, F functor)
Helper function for fire_event.
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
boost::shared_ptr< std::vector< unit_const_ptr > > units_
Definition: dialogs.cpp:100
game_events::t_pump & pump()
Definition: manager.cpp:194
int drain_percent
Percentage of damage recovered as health.
Definition: attack.hpp:77
int hitpoints() const
Definition: types.hpp:123
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:514
bool fire_events()
Fires the sighted events that were earlier recorded by fog/shroud clearing.
Definition: vision.cpp:547
const time_of_day get_illuminated_time_of_day(const unit_map &units, const gamemap &map, const map_location &loc, int for_turn=0) const
Returns time of day object for the passed turn at a location.
void unit_sheath_weapon(const map_location &primary_loc, unit *primary_unit, const attack_type *primary_attack, const attack_type *secondary_attack, const map_location &secondary_loc, unit *secondary_unit)
Play a post-fight animation Both unit can be set to null, only valid units will play their animation...
Definition: udisplay.cpp:518
const std::string & base_id() const
The id of the original type from which this (variation) descended.
Definition: types.hpp:119
GLclampd n
Definition: glew.h:5903
bool is_empty() const
Definition: team.hpp:259
bool find(E event, F functor)
Tests whether an event handler is available.
void attack_expected_damage(double attacker_inflict, double defender_inflict)
Definition: statistics.cpp:416
void attack_unit(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display)
Performs an attack.
Definition: attack.cpp:1384
int min_range() const
Definition: attack_type.hpp:40
bool is_idle() const
Definition: team.hpp:285
bool is_fearless() const
Definition: unit.hpp:306
Standard logging facilities (interface).
bool is_network_ai() const
Definition: team.hpp:270
void attack_result(hit_result res, int damage, int drain)
Definition: statistics.cpp:432
Container associating units to locations.
Definition: map.hpp:90
void set_attacks(int left)
Definition: unit.hpp:233
double defense_weight() const
Definition: attack_type.hpp:48
Class to encapsulate fog/shroud clearing and the resultant sighted events.
Definition: vision.hpp:58
unsigned int num_blows
Effective number of blows, takes swarm into account.
Definition: attack.hpp:79
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1155
unsigned int max_hp
Maximum hitpoints of the unit.
Definition: attack.hpp:73
double average_hp(unsigned int healing=0) const
What's the average hp (weighted average of hp_dist).
boost::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:36
bool is_attacker
True if the unit is the attacker.
Definition: attack.hpp:53
void set_standing(bool with_bars=true)
Sets the animation state to standing.
unsigned int max_experience
Definition: attack.hpp:68
int get_composite_value() const
Definition: abilities.hpp:50
unit_iterator find(size_t id)
Definition: map.cpp:285
virtual bool is_networked_mp() const
bool valid() const
Definition: map.hpp:229
virtual bool local_checkup(const config &expected_data, config &real_data)=0
Compares data to the results calculated during the original game.
#define DBG_NG
Definition: attack.cpp:57
const std::string weapon
bool petrifies
Attack petrifies opponent when it hits.
Definition: attack.hpp:58
static lg::log_domain log_engine("engine")
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
GLdouble s
Definition: glew.h:1358
map_location under_leadership(const unit_map &units, const map_location &loc, int *bonus)
function which tests if the unit at loc is currently affected by leadership.
Definition: attack.cpp:1635
void set_experience(int xp)
Definition: unit.hpp:173
std::vector< int > get_sides_not_seeing(const unit &target)
Returns the sides that cannot currently see target.
Definition: vision.cpp:596
bool drains
Attack drains opponent when it hits.
Definition: attack.hpp:57
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35
Display units performing various actions: moving, attacking, and dying.
int resistance_against(const std::string &damage_name, bool attacker) const
Gets resistance while considering custom WML abilities.
Definition: types.cpp:777
void attack_unit_and_advance(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display, const ai::unit_advancements_aspect &ai_advancement)
Performs an attack, and advanced the units afterwards.
Definition: attack.cpp:1524
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:115
double poisoned
Resulting chance we are poisoned.
battle_context_unit_stats * defender_stats_
Definition: attack.hpp:190
unsigned int swarm_max
Maximum number of blows with swarm (equal to num_blows if swarm isn't used).
Definition: attack.hpp:81
static game_display * get_singleton()
const std::string valid
Little parts of regex templates used to parse Wml annoations.
int combat_modifier(const unit_map &units, const gamemap &map, const map_location &loc, unit_type::ALIGNMENT 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:1649