Add tooltips to unit_attack dialogs (#6462)
In tooltips, the special and weapon_abilities are listed by stats (damage, attacks and chance to hit), and the owner of each is specified.
This commit is contained in:
parent
f7859ad104
commit
81d41a861d
6 changed files with 274 additions and 34 deletions
|
@ -46,9 +46,52 @@
|
|||
|
||||
[window_definition]
|
||||
|
||||
id = tooltip
|
||||
id = tooltip_transparent
|
||||
description = "The window to show a large tooltip."
|
||||
|
||||
[resolution]
|
||||
|
||||
left_border = 15
|
||||
right_border = 15
|
||||
top_border = 15
|
||||
bottom_border = 15
|
||||
|
||||
[background]
|
||||
|
||||
[draw]
|
||||
|
||||
[rectangle]
|
||||
x = 0
|
||||
y = 0
|
||||
w = "(width)"
|
||||
h = "(height)"
|
||||
|
||||
fill_color = "0, 0, 0, 192"
|
||||
border_thickness = 1
|
||||
border_color = "0, 0, 0, 255"
|
||||
|
||||
[/rectangle]
|
||||
|
||||
[/draw]
|
||||
|
||||
[/background]
|
||||
|
||||
[foreground]
|
||||
|
||||
[draw]
|
||||
[/draw]
|
||||
|
||||
[/foreground]
|
||||
|
||||
[/resolution]
|
||||
|
||||
[/window_definition]
|
||||
|
||||
[window_definition]
|
||||
|
||||
id = tooltip
|
||||
description = "The window to show a floating tooltip."
|
||||
|
||||
[resolution]
|
||||
|
||||
left_border = 15
|
||||
|
|
|
@ -141,7 +141,7 @@ def reevaluate_best_size(w, s)
|
|||
|
||||
[window]
|
||||
id = "tooltip"
|
||||
description = "The tooltip popup window with large tooltips, eg in the main menu."
|
||||
description = "The tooltip popup window with floating tooltips."
|
||||
|
||||
[resolution]
|
||||
definition = "tooltip"
|
||||
|
@ -190,6 +190,57 @@ def reevaluate_best_size(w, s)
|
|||
|
||||
[/window]
|
||||
|
||||
[window]
|
||||
id = "tooltip_transparent"
|
||||
description = "The tooltip popup window with floating tooltips, and transparent background."
|
||||
|
||||
[resolution]
|
||||
definition = "tooltip_transparent"
|
||||
|
||||
automatic_placement = false
|
||||
|
||||
functions = "{__GUI_WINDOW_FUNCTIONS}"
|
||||
|
||||
x = "{__GUI_WINDOW_X}"
|
||||
y = "{__GUI_WINDOW_Y}"
|
||||
width = "{__GUI_WINDOW_WIDTH}"
|
||||
height = "{__GUI_WINDOW_HEIGHT}"
|
||||
reevaluate_best_size = "{__GUI_WINDOW_REEVALUATE_BEST_SIZE}"
|
||||
|
||||
# TODO tooltips in this window make little sense.
|
||||
# Have to think of a nice solution.
|
||||
[tooltip]
|
||||
id = "tooltip_transparent"
|
||||
[/tooltip]
|
||||
|
||||
[helptip]
|
||||
id = "tooltip_transparent"
|
||||
[/helptip]
|
||||
|
||||
[grid]
|
||||
|
||||
[row]
|
||||
|
||||
[column]
|
||||
|
||||
[label]
|
||||
id = "label"
|
||||
definition = "default_small"
|
||||
|
||||
use_markup = true
|
||||
wrap = true
|
||||
[/label]
|
||||
|
||||
[/column]
|
||||
|
||||
[/row]
|
||||
|
||||
[/grid]
|
||||
|
||||
[/resolution]
|
||||
|
||||
[/window]
|
||||
|
||||
#undef __GUI_WINDOW_REEVALUATE_BEST_SIZE
|
||||
#undef __GUI_WINDOW_HEIGHT
|
||||
#undef __GUI_WINDOW_WIDTH
|
||||
|
|
|
@ -282,11 +282,11 @@
|
|||
[/linked_group]
|
||||
|
||||
[tooltip]
|
||||
id = "tooltip"
|
||||
id = "tooltip_transparent"
|
||||
[/tooltip]
|
||||
|
||||
[helptip]
|
||||
id = "tooltip"
|
||||
id = "tooltip_transparent"
|
||||
[/helptip]
|
||||
|
||||
[grid]
|
||||
|
|
|
@ -115,19 +115,51 @@ void unit_attack::pre_show(window& window)
|
|||
attacker_itor_->get_location(), false, attacker.weapon
|
||||
);
|
||||
|
||||
const std::set<std::string> checking_tags_other = {"disable", "berserk", "drains", "heal_on_hit", "plague", "slow", "petrifies", "firststrike", "poison"};
|
||||
std::string attw_specials = attacker_weapon.weapon_specials(attacker.backstab_pos);
|
||||
std::string attw_specials_dmg = attacker_weapon.weapon_specials_value({"leadership", "damage"});
|
||||
std::string attw_specials_atk = attacker_weapon.weapon_specials_value({"attacks", "swarm"});
|
||||
std::string attw_specials_cth = attacker_weapon.weapon_specials_value({"chance_to_hit"});
|
||||
std::string attw_specials_others = attacker_weapon.weapon_specials_value(checking_tags_other);
|
||||
bool defender_attack = !(defender_weapon.name().empty() && defender_weapon.damage() == 0 && defender_weapon.num_attacks() == 0 && defender.chance_to_hit == 0);
|
||||
std::string defw_specials = defender_attack ? defender_weapon.weapon_specials() : "";
|
||||
std::string defw_specials_dmg = defender_attack ? defender_weapon.weapon_specials_value({"leadership", "damage"}) : "";
|
||||
std::string defw_specials_atk = defender_attack ? defender_weapon.weapon_specials_value({"attacks", "swarm"}) : "";
|
||||
std::string defw_specials_cth = defender_attack ? defender_weapon.weapon_specials_value({"chance_to_hit"}) : "";
|
||||
std::string defw_specials_others = defender_attack ? defender_weapon.weapon_specials_value(checking_tags_other) : "";
|
||||
|
||||
if(!attw_specials.empty()) {
|
||||
attw_specials = " " + attw_specials;
|
||||
}
|
||||
|
||||
if(!attw_specials_dmg.empty()) {
|
||||
attw_specials_dmg = " " + attw_specials_dmg;
|
||||
}
|
||||
if(!attw_specials_atk.empty()) {
|
||||
attw_specials_atk = " " + attw_specials_atk;
|
||||
}
|
||||
if(!attw_specials_cth.empty()) {
|
||||
attw_specials_cth = " " + attw_specials_cth;
|
||||
}
|
||||
if(!attw_specials_others.empty()) {
|
||||
attw_specials_others = "\n" + ("<b>"+translation::dsgettext("wesnoth", "Other aspects: ")+"</b>") + "\n" + ("<i>"+attw_specials_others+"</i>");
|
||||
}
|
||||
if(!defw_specials.empty()) {
|
||||
defw_specials = " " + defw_specials;
|
||||
}
|
||||
if(!defw_specials_dmg.empty()) {
|
||||
defw_specials_dmg = " " + defw_specials_dmg;
|
||||
}
|
||||
if(!defw_specials_atk.empty()) {
|
||||
defw_specials_atk = " " + defw_specials_atk;
|
||||
}
|
||||
if(!defw_specials_cth.empty()) {
|
||||
defw_specials_cth = " " + defw_specials_cth;
|
||||
}
|
||||
if(!defw_specials_others.empty()) {
|
||||
defw_specials_others = "\n" + ("<b>"+translation::dsgettext("wesnoth", "Other aspects: ")+"</b>") + "\n" + ("<i>"+defw_specials_others+"</i>");
|
||||
}
|
||||
|
||||
std::stringstream attacker_stats, defender_stats;
|
||||
std::stringstream attacker_stats, defender_stats, attacker_tooltip, defender_tooltip;
|
||||
|
||||
// Use attacker/defender.num_blows instead of attacker/defender_weapon.num_attacks() because the latter does not consider the swarm weapon special
|
||||
attacker_stats << "<b>" << attw_name << "</b>" << "\n"
|
||||
|
@ -135,11 +167,23 @@ void unit_attack::pre_show(window& window)
|
|||
<< attw_specials << "\n"
|
||||
<< font::span_color(a_cth_color) << attacker.chance_to_hit << "%</span>";
|
||||
|
||||
attacker_tooltip << translation::dsgettext("wesnoth", "Weapon: ") << "<b>" << attw_name << "</b>" << "\n"
|
||||
<< translation::dsgettext("wesnoth", "Damage: ") << attacker.damage << "<i>" << attw_specials_dmg << "</i>" << "\n"
|
||||
<< translation::dsgettext("wesnoth", "Attacks: ") << attacker.num_blows << "<i>" << attw_specials_atk << "</i>" << "\n"
|
||||
<< translation::dsgettext("wesnoth", "Chance to hit: ") << font::span_color(a_cth_color) << attacker.chance_to_hit << "%</span>"<< "<i>" << attw_specials_cth << "</i>"
|
||||
<< attw_specials_others;
|
||||
|
||||
defender_stats << "<b>" << defw_name << "</b>" << "\n"
|
||||
<< defender.damage << font::weapon_numbers_sep << defender.num_blows
|
||||
<< defw_specials << "\n"
|
||||
<< font::span_color(d_cth_color) << defender.chance_to_hit << "%</span>";
|
||||
|
||||
defender_tooltip << translation::dsgettext("wesnoth", "Weapon: ") << "<b>" << defw_name << "</b>" << "\n"
|
||||
<< translation::dsgettext("wesnoth", "Damage: ") << defender.damage << "<i>" << defw_specials_dmg << "</i>" << "\n"
|
||||
<< translation::dsgettext("wesnoth", "Attacks: ") << defender.num_blows << "<i>" << defw_specials_atk << "</i>" << "\n"
|
||||
<< translation::dsgettext("wesnoth", "Chance to hit: ") << font::span_color(d_cth_color) << defender.chance_to_hit << "%</span>"<< "<i>" << defw_specials_cth << "</i>"
|
||||
<< defw_specials_others;
|
||||
|
||||
std::map<std::string, string_map> data;
|
||||
string_map item;
|
||||
|
||||
|
@ -148,15 +192,19 @@ void unit_attack::pre_show(window& window)
|
|||
item["label"] = attacker_weapon.icon();
|
||||
data.emplace("attacker_weapon_icon", item);
|
||||
|
||||
item["tooltip"] = attacker_tooltip.str();
|
||||
item["label"] = attacker_stats.str();
|
||||
data.emplace("attacker_weapon", item);
|
||||
item["tooltip"] = "";
|
||||
|
||||
item["label"] = "<span color='#a69275'>" + font::unicode_em_dash + " " + range + " " + font::unicode_em_dash + "</span>";
|
||||
data.emplace("range", item);
|
||||
|
||||
item["tooltip"] = defender_attack ? defender_tooltip.str() : "";
|
||||
item["label"] = defender_stats.str();
|
||||
data.emplace("defender_weapon", item);
|
||||
|
||||
item["tooltip"] = "";
|
||||
item["label"] = defender_weapon.icon();
|
||||
data.emplace("defender_weapon_icon", item);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "display_context.hpp"
|
||||
#include "font/text_formatting.hpp"
|
||||
#include "game_board.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "global.hpp"
|
||||
#include "lexical_cast.hpp"
|
||||
#include "log.hpp"
|
||||
|
@ -834,24 +835,18 @@ std::vector<std::pair<t_string, t_string>> attack_type::special_tooltips(
|
|||
/**
|
||||
* static used in weapon_specials (bool only_active, bool is_backstab) and
|
||||
* @return a string and a set_string for the weapon_specials function below.
|
||||
* @param[in,out] weapon_abilities the string modified and returned
|
||||
* @param[in,out] temp_string the string modified and returned
|
||||
* @param[in] active the boolean for determine if @name can be added or not
|
||||
* @param[in] sp reference to ability to check
|
||||
* @param[in] name string who must be or not added
|
||||
* @param[in,out] checking_name the reference for checking if @name already added
|
||||
*/
|
||||
static void add_name(std::string& weapon_abilities, bool active, const config::any_child sp, std::set<std::string>& checking_name, bool affect_adjacent)
|
||||
static void add_name(std::string& temp_string, bool active, const std::string name, std::set<std::string>& checking_name)
|
||||
{
|
||||
if (active) {
|
||||
const std::string& name = sp.cfg["name"].str();
|
||||
|
||||
if (!name.empty() && checking_name.count(name) == 0) {
|
||||
checking_name.insert(name);
|
||||
if (!weapon_abilities.empty()) weapon_abilities += ", ";
|
||||
if (affect_adjacent) {
|
||||
weapon_abilities += sp.cfg["affect_enemies"].to_bool() ? font::span_color(font::BAD_COLOR, name) : font::span_color(font::GOOD_COLOR, name);
|
||||
} else {
|
||||
weapon_abilities += font::span_color(font::BUTTON_COLOR, name);
|
||||
}
|
||||
if (!temp_string.empty()) temp_string += ", ";
|
||||
temp_string += font::span_color(font::BUTTON_COLOR, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -883,35 +878,102 @@ std::string attack_type::weapon_specials(bool is_backstab) const
|
|||
if (!active) res += "</span>";
|
||||
}
|
||||
}
|
||||
std::string weapon_abilities;
|
||||
std::string temp_string;
|
||||
std::set<std::string> checking_name;
|
||||
weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name);
|
||||
weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, {}, "affect_allies");
|
||||
if(!temp_string.empty() && !res.empty()) {
|
||||
temp_string = ", \n" + temp_string;
|
||||
res += temp_string;
|
||||
} else if (!temp_string.empty()){
|
||||
res = temp_string;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void add_name_list(std::string& temp_string, std::string& weapon_abilities, std::set<std::string>& checking_name, const std::string from_str)
|
||||
{
|
||||
if(!temp_string.empty()){
|
||||
temp_string = translation::dsgettext("wesnoth", from_str.c_str()) + temp_string;
|
||||
weapon_abilities += (!weapon_abilities.empty() && !temp_string.empty()) ? "\n" : "";
|
||||
weapon_abilities += temp_string;
|
||||
temp_string.clear();
|
||||
checking_name.clear();
|
||||
}
|
||||
}
|
||||
|
||||
std::string attack_type::weapon_specials_value(const std::set<std::string> checking_tags) const
|
||||
{
|
||||
//log_scope("weapon_specials_value");
|
||||
std::string temp_string, weapon_abilities;
|
||||
std::set<std::string> checking_name;
|
||||
for (const config::any_child sp : specials_.all_children_range()) {
|
||||
if((checking_tags.count(sp.key) != 0)){
|
||||
const bool active = special_active(sp.cfg, AFFECT_SELF, sp.key);
|
||||
add_name(temp_string, active, sp.cfg["name"].str(), checking_name);
|
||||
}
|
||||
}
|
||||
add_name_list(temp_string, weapon_abilities, checking_name, "");
|
||||
|
||||
weapon_specials_impl_self(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags, true);
|
||||
add_name_list(temp_string, weapon_abilities, checking_name, "Owned: ");
|
||||
|
||||
weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags, "affect_allies", true);
|
||||
add_name_list(temp_string, weapon_abilities, checking_name, "Taught: ");
|
||||
|
||||
weapon_specials_impl_adj(temp_string, self_, shared_from_this(), other_attack_, self_loc_, AFFECT_SELF, checking_name, checking_tags, "affect_enemies", true);
|
||||
add_name_list(temp_string, weapon_abilities, checking_name, "Taught: (by an enemy): ");
|
||||
|
||||
|
||||
if(other_attack_) {
|
||||
for (const config::any_child sp : other_attack_->specials_.all_children_range()) {
|
||||
if((checking_tags.count(sp.key) != 0)){
|
||||
const bool active = other_attack_->special_active(sp.cfg, AFFECT_OTHER, sp.key);
|
||||
add_name(temp_string, active, sp.cfg["name"].str(), checking_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
weapon_specials_impl_self(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
|
||||
weapon_specials_impl_adj(temp_string, other_, other_attack_, shared_from_this(), other_loc_, AFFECT_OTHER, checking_name, checking_tags);
|
||||
add_name_list(temp_string, weapon_abilities, checking_name, "Used by opponent: ");
|
||||
|
||||
return weapon_abilities;
|
||||
}
|
||||
|
||||
void attack_type::weapon_specials_impl_self(std::string& temp_string, unit_const_ptr self, const_attack_ptr self_attack, const_attack_ptr other_attack, const map_location& self_loc, AFFECTS whom,
|
||||
std::set<std::string>& checking_name, const std::set<std::string>& checking_tags, bool leader_bool)
|
||||
{
|
||||
if(self){
|
||||
for (const config::any_child sp : self->abilities().all_children_range()){
|
||||
bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(sp.key) != 0) : true;
|
||||
const bool active = tag_checked && check_self_abilities_impl(self_attack, other_attack, sp.cfg, self, self_loc, whom, sp.key, leader_bool);
|
||||
add_name(temp_string, active, sp.cfg["name"].str(), checking_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void attack_type::weapon_specials_impl_adj(std::string& temp_string, unit_const_ptr self, const_attack_ptr self_attack, const_attack_ptr other_attack, const map_location& self_loc, AFFECTS whom,
|
||||
std::set<std::string>& checking_name, const std::set<std::string>& checking_tags, const std::string& affect_adjacents, bool leader_bool)
|
||||
{
|
||||
assert(display::get_singleton());
|
||||
const unit_map& units = display::get_singleton()->get_units();
|
||||
if(self_){
|
||||
for (const config::any_child sp : self_->abilities().all_children_range()){
|
||||
const bool active = check_self_abilities_impl(shared_from_this(), other_attack_, sp.cfg, self_, self_loc_, AFFECT_SELF, sp.key);
|
||||
add_name(weapon_abilities, active, sp, checking_name, false);
|
||||
}
|
||||
const auto adjacent = get_adjacent_tiles(self_loc_);
|
||||
if(self){
|
||||
const auto adjacent = get_adjacent_tiles(self_loc);
|
||||
for(unsigned i = 0; i < adjacent.size(); ++i) {
|
||||
const unit_map::const_iterator it = units.find(adjacent[i]);
|
||||
if (it == units.end() || it->incapacitated())
|
||||
continue;
|
||||
if(&*it == self_.get())
|
||||
if(&*it == self.get())
|
||||
continue;
|
||||
for (const config::any_child sp : it->abilities().all_children_range()){
|
||||
const bool active = check_adj_abilities_impl(shared_from_this(), other_attack_, sp.cfg, self_, *it, i, self_loc_, AFFECT_SELF, sp.key);
|
||||
add_name(weapon_abilities, active, sp, checking_name, true);
|
||||
bool tag_checked = (!checking_tags.empty()) ? (checking_tags.count(sp.key) != 0) : true;
|
||||
bool default_bool = (affect_adjacents == "affect_allies") ? true : false;
|
||||
bool affect_allies = (!affect_adjacents.empty()) ? sp.cfg[affect_adjacents].to_bool(default_bool) : true;
|
||||
const bool active = tag_checked && check_adj_abilities_impl(self_attack, other_attack, sp.cfg, self, *it, i, self_loc, whom, sp.key, leader_bool) && affect_allies;
|
||||
add_name(temp_string, active, sp.cfg["name"].str(), checking_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!weapon_abilities.empty() && !res.empty()) {
|
||||
weapon_abilities = ", \n" + weapon_abilities;
|
||||
res += weapon_abilities;
|
||||
} else if (!weapon_abilities.empty()){
|
||||
res = weapon_abilities;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -83,6 +83,7 @@ public:
|
|||
unit_ability_list get_specials(const std::string& special) const;
|
||||
std::vector<std::pair<t_string, t_string>> special_tooltips(boost::dynamic_bitset<>* active_list = nullptr) const;
|
||||
std::string weapon_specials(bool is_backstab=false) const;
|
||||
std::string weapon_specials_value(const std::set<std::string> checking_tags) const;
|
||||
|
||||
/** Calculates the number of attacks this weapon has, considering specials. */
|
||||
void modified_attacks(bool is_backstab, unsigned & min_attacks,
|
||||
|
@ -150,6 +151,41 @@ private:
|
|||
bool special_active(const config& special, AFFECTS whom, const std::string& tag_name,
|
||||
bool include_backstab=true, const std::string& filter_self ="filter_self") const;
|
||||
|
||||
/** weapon_specials_impl_self and weapon_specials_impl_adj : check if special name can be added.
|
||||
* @param[in,out] temp_string the string modified and returned
|
||||
* @param[in] self the unit checked.
|
||||
* @param[in] self_attack the attack used by unit checked in this function.
|
||||
* @param[in] other_attack the attack used by opponent to unit checked.
|
||||
* @param[in] self_loc location of the unit checked.
|
||||
* @param[in] whom determine if unit affected or not by special ability.
|
||||
* @param[in,out] checking_name the reference for checking if a name is already added
|
||||
* @param[in] checking_tags the reference for checking if special ability type can be used
|
||||
* @param[in] leader_bool If true, [leadership] abilities are checked.
|
||||
*/
|
||||
static void weapon_specials_impl_self(
|
||||
std::string& temp_string,
|
||||
unit_const_ptr self,
|
||||
const_attack_ptr self_attack,
|
||||
const_attack_ptr other_attack,
|
||||
const map_location& self_loc,
|
||||
AFFECTS whom,
|
||||
std::set<std::string>& checking_name,
|
||||
const std::set<std::string>& checking_tags={},
|
||||
bool leader_bool=false
|
||||
);
|
||||
|
||||
static void weapon_specials_impl_adj(
|
||||
std::string& temp_string,
|
||||
unit_const_ptr self,
|
||||
const_attack_ptr self_attack,
|
||||
const_attack_ptr other_attack,
|
||||
const map_location& self_loc,
|
||||
AFFECTS whom,
|
||||
std::set<std::string>& checking_name,
|
||||
const std::set<std::string>& checking_tags={},
|
||||
const std::string& affect_adjacents="",
|
||||
bool leader_bool=false
|
||||
);
|
||||
/** check_self_abilities_impl : return an boolean value for checking of activities of abilities used like weapon
|
||||
* @return True if the special @a tag_name is active.
|
||||
* @param self_attack the attack used by unit checked in this function.
|
||||
|
|
Loading…
Add table
Reference in a new issue