The Battle for Wesnoth  1.17.12+dev
unit_attack.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2022
3  by Mark de Wever <koraq@xs4all.nl>
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 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "font/text_formatting.hpp"
23 #include "gui/widgets/button.hpp"
24 #include "gui/widgets/label.hpp"
25 #include "gui/widgets/image.hpp"
26 #include "gui/widgets/listbox.hpp"
27 #include "gui/widgets/settings.hpp"
29 #include "gui/widgets/window.hpp"
30 #include "game_config.hpp"
31 #include "game_display.hpp"
32 #include "gettext.hpp"
33 #include "help/help.hpp"
34 #include "language.hpp"
35 #include "color.hpp"
36 #include "units/unit.hpp"
37 
38 #include <functional>
39 
40 namespace gui2::dialogs
41 {
42 
44 
45 unit_attack::unit_attack(const unit_map::iterator& attacker_itor,
46  const unit_map::iterator& defender_itor,
47  std::vector<battle_context>&& weapons,
48  const int best_weapon)
49  : modal_dialog(window_id())
50  , selected_weapon_(-1)
51  , attacker_itor_(attacker_itor)
52  , defender_itor_(defender_itor)
53  , weapons_(std::move(weapons))
54  , best_weapon_(best_weapon)
55 {
56 }
57 
59 {
60  const std::size_t index = find_widget<listbox>(get_window(), "weapon_list", false).get_selected_row();
61  attack_predictions::display(weapons_[index], attacker_itor_.get_shared_ptr(), defender_itor_.get_shared_ptr());
62 }
63 
65 {
67  find_widget<button>(&window, "damage_calculation", false),
68  std::bind(&unit_attack::damage_calc_callback, this));
69 
70  find_widget<unit_preview_pane>(&window, "attacker_pane", false)
71  .set_displayed_unit(*attacker_itor_);
72 
73  find_widget<unit_preview_pane>(&window, "defender_pane", false)
74  .set_displayed_unit(*defender_itor_);
75 
76  selected_weapon_ = -1;
77 
78  listbox& weapon_list = find_widget<listbox>(&window, "weapon_list", false);
79  window.keyboard_capture(&weapon_list);
80 
81  // Possible TODO: If a "blank weapon" is generally useful, add it as a static member in attack_type.
82  static const config empty;
83  static const_attack_ptr no_weapon(new attack_type(empty));
84 
85  for(const auto & weapon : weapons_) {
86  const battle_context_unit_stats& attacker = weapon.get_attacker_stats();
87  const battle_context_unit_stats& defender = weapon.get_defender_stats();
88 
89  const attack_type& attacker_weapon =
90  *attacker.weapon;
91  const attack_type& defender_weapon = defender.weapon ?
92  *defender.weapon : *no_weapon;
93 
94  const color_t a_cth_color = game_config::red_to_green(attacker.chance_to_hit);
95  const color_t d_cth_color = game_config::red_to_green(defender.chance_to_hit);
96 
97  const std::string attw_name = !attacker_weapon.name().empty() ? attacker_weapon.name() : " ";
98  const std::string defw_name = !defender_weapon.name().empty() ? defender_weapon.name() : " ";
99 
100  std::string range = attacker_weapon.range().empty() ? defender_weapon.range() : attacker_weapon.range();
101  if (!range.empty()) {
102  range = string_table["range_" + range];
103  }
104 
105  auto a_ctx = attacker_weapon.specials_context(
108  attacker_itor_->get_location(),
109  defender_itor_->get_location(), true, defender.weapon
110  );
111 
112  auto d_ctx = defender_weapon.specials_context(
115  defender_itor_->get_location(),
116  attacker_itor_->get_location(), false, attacker.weapon
117  );
118 
119  const std::set<std::string> checking_tags_other = {"disable", "berserk", "drains", "heal_on_hit", "plague", "slow", "petrifies", "firststrike", "poison"};
120  std::string attw_specials = attacker_weapon.weapon_specials();
121  std::string attw_specials_dmg = attacker_weapon.weapon_specials_value({"leadership", "damage"});
122  std::string attw_specials_atk = attacker_weapon.weapon_specials_value({"attacks", "swarm"});
123  std::string attw_specials_cth = attacker_weapon.weapon_specials_value({"chance_to_hit"});
124  std::string attw_specials_others = attacker_weapon.weapon_specials_value(checking_tags_other);
125  bool defender_attack = !(defender_weapon.name().empty() && defender_weapon.damage() == 0 && defender_weapon.num_attacks() == 0 && defender.chance_to_hit == 0);
126  std::string defw_specials = defender_attack ? defender_weapon.weapon_specials() : "";
127  std::string defw_specials_dmg = defender_attack ? defender_weapon.weapon_specials_value({"leadership", "damage"}) : "";
128  std::string defw_specials_atk = defender_attack ? defender_weapon.weapon_specials_value({"attacks", "swarm"}) : "";
129  std::string defw_specials_cth = defender_attack ? defender_weapon.weapon_specials_value({"chance_to_hit"}) : "";
130  std::string defw_specials_others = defender_attack ? defender_weapon.weapon_specials_value(checking_tags_other) : "";
131 
132  if(!attw_specials.empty()) {
133  attw_specials = " " + attw_specials;
134  }
135  if(!attw_specials_dmg.empty()) {
136  attw_specials_dmg = " " + attw_specials_dmg;
137  }
138  if(!attw_specials_atk.empty()) {
139  attw_specials_atk = " " + attw_specials_atk;
140  }
141  if(!attw_specials_cth.empty()) {
142  attw_specials_cth = " " + attw_specials_cth;
143  }
144  if(!attw_specials_others.empty()) {
145  attw_specials_others = "\n" + ("<b>"+translation::dsgettext("wesnoth", "Other aspects: ")+"</b>") + "\n" + ("<i>"+attw_specials_others+"</i>");
146  }
147  if(!defw_specials.empty()) {
148  defw_specials = " " + defw_specials;
149  }
150  if(!defw_specials_dmg.empty()) {
151  defw_specials_dmg = " " + defw_specials_dmg;
152  }
153  if(!defw_specials_atk.empty()) {
154  defw_specials_atk = " " + defw_specials_atk;
155  }
156  if(!defw_specials_cth.empty()) {
157  defw_specials_cth = " " + defw_specials_cth;
158  }
159  if(!defw_specials_others.empty()) {
160  defw_specials_others = "\n" + ("<b>"+translation::dsgettext("wesnoth", "Other aspects: ")+"</b>") + "\n" + ("<i>"+defw_specials_others+"</i>");
161  }
162 
163  std::stringstream attacker_stats, defender_stats, attacker_tooltip, defender_tooltip;
164 
165  // Use attacker/defender.num_blows instead of attacker/defender_weapon.num_attacks() because the latter does not consider the swarm weapon special
166  attacker_stats << "<b>" << attw_name << "</b>" << "\n"
167  << attacker.damage << font::weapon_numbers_sep << attacker.num_blows
168  << attw_specials << "\n"
169  << font::span_color(a_cth_color) << attacker.chance_to_hit << "%</span>";
170 
171  attacker_tooltip << translation::dsgettext("wesnoth", "Weapon: ") << "<b>" << attw_name << "</b>" << "\n"
172  << translation::dsgettext("wesnoth", "Damage: ") << attacker.damage << "<i>" << attw_specials_dmg << "</i>" << "\n"
173  << translation::dsgettext("wesnoth", "Attacks: ") << attacker.num_blows << "<i>" << attw_specials_atk << "</i>" << "\n"
174  << translation::dsgettext("wesnoth-lib", "Chance to hit") << ": " << font::span_color(a_cth_color) << attacker.chance_to_hit << "%</span>"<< "<i>" << attw_specials_cth << "</i>"
175  << attw_specials_others;
176 
177  defender_stats << "<b>" << defw_name << "</b>" << "\n"
178  << defender.damage << font::weapon_numbers_sep << defender.num_blows
179  << defw_specials << "\n"
180  << font::span_color(d_cth_color) << defender.chance_to_hit << "%</span>";
181 
182  defender_tooltip << translation::dsgettext("wesnoth", "Weapon: ") << "<b>" << defw_name << "</b>" << "\n"
183  << translation::dsgettext("wesnoth", "Damage: ") << defender.damage << "<i>" << defw_specials_dmg << "</i>" << "\n"
184  << translation::dsgettext("wesnoth", "Attacks: ") << defender.num_blows << "<i>" << defw_specials_atk << "</i>" << "\n"
185  << translation::dsgettext("wesnoth-lib", "Chance to hit") << ": " << font::span_color(d_cth_color) << defender.chance_to_hit << "%</span>"<< "<i>" << defw_specials_cth << "</i>"
186  << defw_specials_others;
187 
190 
191  item["use_markup"] = "true";
192 
193  item["label"] = attacker_weapon.icon();
194  data.emplace("attacker_weapon_icon", item);
195 
196  item["tooltip"] = attacker_tooltip.str();
197  item["label"] = attacker_stats.str();
198  data.emplace("attacker_weapon", item);
199  item["tooltip"] = "";
200 
201  item["label"] = "<span color='#a69275'>" + font::unicode_em_dash + " " + range + " " + font::unicode_em_dash + "</span>";
202  data.emplace("range", item);
203 
204  item["tooltip"] = defender_attack ? defender_tooltip.str() : "";
205  item["label"] = defender_stats.str();
206  data.emplace("defender_weapon", item);
207 
208  item["tooltip"] = "";
209  item["label"] = defender_weapon.icon();
210  data.emplace("defender_weapon_icon", item);
211 
212  weapon_list.add_row(data);
213  }
214 
215  // If these two aren't the same size, we can't use list selection incides
216  // to access to weapons list!
217  assert(weapons_.size() == weapon_list.get_item_count());
218 
219  weapon_list.select_row(best_weapon_);
220 }
221 
223 {
224  if(get_retval() == retval::OK) {
225  selected_weapon_ = find_widget<listbox>(&window, "weapon_list", false).get_selected_row();
226  }
227 }
228 
229 } // namespace dialogs
const_attack_ptr weapon
The weapon used by the unit to attack the opponent, or nullptr if there is none.
Definition: attack.hpp:53
window(const builder_window::window_resolution &definition)
< Needs to be initialized in show.
Definition: window.cpp:263
std::string weapon_specials_value(const std::set< std::string > checking_tags) const
Definition: abilities.cpp:928
int best_weapon_
The best weapon, aka the one high-lighted.
Definition: unit_attack.hpp:78
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:179
This file contains the window object, this object is a top level container which has the event manage...
std::string_view data
Definition: picture.cpp:206
grid & add_row(const widget_item &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:61
STL namespace.
int num_attacks() const
Definition: attack_type.hpp:54
unsigned int chance_to_hit
Effective chance to hit as a percentage (all factors accounted for).
Definition: attack.hpp:72
std::string dsgettext(const char *domainname, const char *msgid)
Definition: gettext.cpp:437
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:245
std::vector< battle_context > weapons_
List of all battle contexts used for getting the weapons.
Definition: unit_attack.hpp:75
The listbox class.
Definition: listbox.hpp:45
std::string span_color(const color_t &color)
Returns a Pango formatting string using the provided color_t object.
int damage
Effective damage of the weapon (all factors accounted for).
Definition: attack.hpp:73
std::map< std::string, t_string > widget_item
Definition: widget.hpp:32
std::string weapon_specials() const
Returns a comma-separated string of active names for the specials of *this.
Definition: abilities.cpp:885
This file contains the settings handling of the widget library.
const t_string & name() const
Definition: attack_type.hpp:43
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:58
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:167
virtual void post_show(window &window) override
Actions to be taken after the window has been shown.
const std::string & range() const
Definition: attack_type.hpp:47
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:126
void unit_attack(display *disp, game_board &board, const map_location &a, const map_location &b, int damage, const attack_type &attack, const_attack_ptr secondary_attack, int swing, const std::string &hit_text, int drain_amount, const std::string &att_text, const std::vector< std::string > *extra_hit_sounds, bool attacking)
Make the unit on tile &#39;a&#39; attack the unit on tile &#39;b&#39;.
Definition: udisplay.cpp:599
const std::string & icon() const
Definition: attack_type.hpp:46
Structure describing the statistics of a unit involved in the battle.
Definition: attack.hpp:51
window * get_window()
Returns a pointer to the dialog&#39;s window.
pointer get_shared_ptr() const
This is exactly the same as operator-> but it&#39;s slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:218
int damage() const
Definition: attack_type.hpp:53
int selected_weapon_
The index of the selected weapon.
Definition: unit_attack.hpp:66
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
int get_retval() const
Returns the cached window exit code.
bool empty() const
Definition: tstring.hpp:187
const std::string weapon_numbers_sep
Definition: constants.cpp:49
This shows the dialog for attacking units.
Definition: unit_attack.hpp:41
symbol_table string_table
Definition: language.cpp:65
const std::string unicode_em_dash
Definition: constants.cpp:44
unit_map::iterator attacker_itor_
Iterator pointing to the attacker.
Definition: unit_attack.hpp:69
Abstract base class for all modal dialogs.
Container associating units to locations.
Definition: map.hpp:98
color_t red_to_green(double val, bool for_text)
Return a color corresponding to the value val red for val=0.0 to green for val=100.0, passing by yellow.
unsigned int num_blows
Effective number of blows, takes swarm into account.
Definition: attack.hpp:77
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:35
specials_context_t specials_context(unit_const_ptr self, unit_const_ptr other, const map_location &unit_loc, const map_location &other_loc, bool attacking, const_attack_ptr other_attack) const
Dialog was closed with the OK button.
Definition: retval.hpp:35
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
std::shared_ptr< const attack_type > const_attack_ptr
Definition: ptr.hpp:34
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: unit_attack.cpp:64
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:66
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:414
unit_map::iterator defender_itor_
Iterator pointing to the defender.
Definition: unit_attack.hpp:72