Colored attack type icons, resistances and movement costs. Reordered resistances. (#5264)

Using colors to make relevant information stand out at a quick glance.

The six attack type icons are recolored using the six equidistant hues cyan, green, yellow, red, magenta and blue, with the same saturation and lightness as the old icons. The images are optimized with woptipng.

Resistances and movement costs are colored using a gradient from red to green, like the defense values.

The resistance table in the help browser now also shows the attack type icons.

Previously, the resistance table was always ordered alphabetically by their original English names (not their translation, unlike the terrain modifiers). Now, the order is set to blade - pierce - impact - fire - cold - arcane.

These changes are made in all relevant areas, including the help browser, tooltips, the sidebar and dialogs.

Missing icons are handled by replacing them with a blank image scaled to the same size.
This commit is contained in:
walodar 2022-08-02 00:57:46 +02:00 committed by GitHub
parent efafebb1df
commit 6eb2246eff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 181 additions and 134 deletions

View file

@ -1812,6 +1812,9 @@
[entry]
name = "vn971, vgaming"
[/entry]
[entry]
name = "walodar"
[/entry]
[entry]
name = "Wedge009"
[/entry]

View file

@ -38,8 +38,8 @@
flag_rgb=flag_green
unit_rgb=magenta
red_green_scale="e60000,ff0000,ff4000,ff8000,ffc000,ffff00,c0ff00,80ff00,40ff00,00ff00,00e600"
red_green_scale_text="c80000,dd0000,dd3700,dd6e00,dda500,dddd00,a5dd00,6edd00,37dd00,00dd00,00c800"
red_green_scale="df0000,e20000,e40000,e70000,ea0000,ec0000,ef0000,f20000,f40000,f70000,fa0000,fc0000,ff0000,ff0500,ff0b00,ff1000,ff1500,ff1b00,ff2000,ff2500,ff2b00,ff3000,ff3500,ff3a00,ff4000,ff4500,ff4a00,ff5000,ff5500,ff5a00,ff6000,ff6500,ff6a00,ff7000,ff7500,ff7a00,ff8000,ff8500,ff8a00,ff8f00,ff9500,ff9a00,ff9f00,ffa500,ffaa00,ffaf00,ffb500,ffba00,ffbf00,ffc500,ffca00,ffcf00,ffd500,ffda00,ffdf00,ffe400,ffea00,ffef00,fff400,fffa00,ffff00,faff00,f4ff00,efff00,eaff00,e4ff00,dfff00,daff00,d5ff00,cfff00,caff00,c5ff00,bfff00,baff00,b5ff00,afff00,aaff00,a5ff00,9fff00,9aff00,95ff00,8fff00,8aff00,85ff00,80ff00,7aff00,75ff00,70ff00,6aff00,65ff00,60ff00,5aff00,55ff00,50ff00,4aff00,45ff00,40ff00,3aff00,35ff00,30ff00,2bff00,25ff00,20ff00,1bff00,15ff00,10ff00,0bff00,05ff00,00ff00,00fc00,00fa00,00f700,00f400,00f200,00ef00,00ec00,00ea00,00e700,00e400,00e200,00df00"
red_green_scale_text="c30000,c60000,c80000,ca0000,cd0000,cf0000,d10000,d40000,d60000,d80000,da0000,dd0000,df0000,df0500,df0900,df0e00,df1300,df1700,df1c00,df2100,df2500,df2a00,df2e00,df3300,df3800,df3c00,df4100,df4600,df4a00,df4f00,df5400,df5800,df5d00,df6200,df6600,df6b00,df7000,df7400,df7900,df7e00,df8200,df8700,df8b00,df9000,df9500,df9900,df9e00,dfa300,dfa700,dfac00,dfb100,dfb500,dfba00,dfbf00,dfc300,dfc800,dfcd00,dfd100,dfd600,dfda00,dfdf00,dadf00,d6df00,d1df00,cddf00,c8df00,c3df00,bfdf00,badf00,b5df00,b1df00,acdf00,a7df00,a3df00,9edf00,99df00,95df00,90df00,8bdf00,87df00,82df00,7edf00,79df00,74df00,70df00,6bdf00,66df00,62df00,5ddf00,58df00,54df00,4fdf00,4adf00,46df00,41df00,3cdf00,38df00,33df00,2edf00,2adf00,25df00,21df00,1cdf00,17df00,13df00,0edf00,09df00,05df00,00df00,00dd00,00da00,00d800,00d600,00d400,00d100,00cf00,00cd00,00ca00,00c800,00c600,00c300"
blue_white_scale="00A0E1,FFFFFF,96FFFF,00CDCD,FF00E1,A91EFF,8B00ED,AA00FF"
blue_white_scale_text="8B00ED,AA00FF,A91EFF,E11EFF,00A0FF,00CDCD,96FFFF,FFFFFF,FFFFFF,FFFFFF,FFFFFF,FFFFFF,FFFFFF,FFFFFF,FFFFFF,FFFFFF"
@ -93,6 +93,7 @@
ellipsis="misc/icon-ellipsis.png"
missing="misc/missing-image.png"
blank="misc/blank.png~CROP(0, 0, 72, 72)"
[/images]
[sounds]

View file

@ -102,6 +102,7 @@
{SIMPLE_KEY level string}
{SIMPLE_KEY ellipsis string}
{SIMPLE_KEY missing string}
{SIMPLE_KEY blank string}
{SIMPLE_KEY battery_icon string}
{SIMPLE_KEY time_icon string}
[/tag]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 251 B

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 372 B

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 B

After

Width:  |  Height:  |  Size: 351 B

View file

@ -227,6 +227,7 @@ std::string
level,
ellipsis,
missing,
blank,
// notifications icon
app_icon = "images/icons/icon-game.png";
@ -360,6 +361,7 @@ void load_config(const config &v)
level = i["level"].str();
ellipsis = i["ellipsis"].str();
missing = i["missing"].str();
blank = i["blank"].str();
} // images
hp_bar_scaling = v["hp_bar_scaling"].to_double(0.666);
@ -555,22 +557,22 @@ const std::vector<color_t>& tc_info(const std::string& name)
return tc_info(name);
}
color_t red_to_green(int val, bool for_text)
color_t red_to_green(double val, bool for_text)
{
const std::vector<color_t>& color_scale = for_text ? red_green_scale_text : red_green_scale;
val = std::clamp(val, 0, 100);
const int lvl = (color_scale.size() - 1) * val / 100;
const double val_scaled = std::clamp(0.01 * val, 0.0, 1.0);
const int lvl = std::nearbyint((color_scale.size() - 1) * val_scaled);
return color_scale[lvl];
}
color_t blue_to_white(int val, bool for_text)
color_t blue_to_white(double val, bool for_text)
{
const std::vector<color_t>& color_scale = for_text ? blue_white_scale_text : blue_white_scale;
val = std::clamp(val, 0, 100);
const int lvl = (color_scale.size() - 1) * val / 100;
const double val_scaled = std::clamp(0.01 * val, 0.0, 1.0);
const int lvl = std::nearbyint((color_scale.size() - 1) * val_scaled);
return color_scale[lvl];
}

View file

@ -144,6 +144,7 @@ namespace game_config
level,
ellipsis,
missing,
blank,
// notifications icon
app_icon;
} //images
@ -202,13 +203,12 @@ namespace game_config
/**
* Return a color corresponding to the value val
* red for val=0 to green for val=100, passing by yellow.
* red for val=0.0 to green for val=100.0, passing by yellow.
* Colors are defined by [game_config] keys
* red_green_scale and red_green_scale_text
*/
color_t red_to_green(int val, bool for_text = true);
color_t blue_to_white(int val, bool for_text = true);
color_t red_to_green(double val, bool for_text = true);
color_t blue_to_white(double val, bool for_text = true);
std::string get_default_title_string();
}

View file

@ -110,14 +110,14 @@ static inline tree_view_node& add_name_tree_node(tree_view_node& header_node, co
}
static inline std::string get_hp_tooltip(
const utils::string_map& res, const std::function<int(const std::string&, bool)>& get)
const utils::string_map_res& res, const std::function<int(const std::string&, bool)>& get)
{
std::ostringstream tooltip;
std::set<std::string> resistances_table;
std::vector<std::string> resistances_table;
bool att_def_diff = false;
for(const utils::string_map::value_type &resist : res) {
for(const utils::string_map_res::value_type &resist : res) {
std::ostringstream line;
line << translation::dgettext("wesnoth", resist.first.c_str()) << ": ";
@ -133,7 +133,7 @@ static inline std::string get_hp_tooltip(
att_def_diff = true;
}
resistances_table.insert(line.str());
resistances_table.push_back(line.str());
}
tooltip << "<big>" << _("Resistances: ") << "</big>";
@ -176,26 +176,29 @@ static inline std::string get_mp_tooltip(int total_movement, std::function<int (
tooltip << '\n' << font::unicode_bullet << " " << tm.name << ": ";
// movement - range: 1 .. 5, movetype::UNREACHABLE=impassable
const bool cannot_move = tm.moves > total_movement;
const bool cannot_move = tm.moves > total_movement; // cannot move in this terrain
double movement_red_to_green = 100.0 - 25.0 * tm.moves;
std::string color;
if(cannot_move) {
// cannot move in this terrain
color = "red";
} else if(tm.moves > 1) {
color = "yellow";
} else {
color = "white";
}
// passing false to select the more saturated red-to-green scale
std::string color = game_config::red_to_green(movement_red_to_green, false).to_hex_string();
tooltip << "<span color='" << color << "'>";
// A 5 MP margin; if the movement costs go above the unit's max moves + 5, we replace it with dashes.
if(cannot_move && (tm.moves > total_movement + 5)) {
tooltip << font::unicode_figure_dash;
} else if (cannot_move) {
tooltip << "(" << tm.moves << ")";
} else {
tooltip << tm.moves;
}
if(tm.moves != 0) {
const int movement_hexes_per_turn = total_movement / tm.moves;
tooltip << " ";
for(int i = 0; i < movement_hexes_per_turn; ++i) {
tooltip << "\u2b23"; // Unicode horizontal black hexagon
}
}
tooltip << "</span>";
}
@ -221,8 +224,8 @@ void unit_preview_pane::print_attack_details(T attacks, tree_view_node& parent_n
);
for(const auto& a : attacks) {
const std::string range_png = std::string("icons/profiles/") + a.range() + "_attack.png~SCALE_INTO_SHARP(16,16)";
const std::string type_png = std::string("icons/profiles/") + a.type() + ".png~SCALE_INTO_SHARP(16,16)";
const std::string range_png = std::string("icons/profiles/") + a.range() + "_attack.png~SCALE_INTO(16,16)";
const std::string type_png = std::string("icons/profiles/") + a.type() + ".png~SCALE_INTO(16,16)";
const bool range_png_exists = ::image::locator(range_png).file_exists();
const bool type_png_exists = ::image::locator(type_png).file_exists();

View file

@ -604,6 +604,9 @@ std::string unit_topic_generator::operator()() const {
}
}
// Padding for range and damage type icons
const auto padding = 4; // matches the alignment of the terrain rows
// Print the different attacks a unit has, if it has any.
if (!type_.attacks().empty()) {
// Print headers for the table.
@ -641,11 +644,8 @@ std::string unit_topic_generator::operator()() const {
push_tab_pair(row, attack_ss.str());
attack_ss.str(clear_stringstream);
// Padding for range and damage type icons
const auto padding = 5; // TODO amount of padding?
// Range, with icon
const std::string range_icon = "icons/profiles/" + attack.range() + "_attack.png";
const std::string range_icon = "icons/profiles/" + attack.range() + "_attack.png~SCALE_INTO(16,16)";
if (attack.min_range() > 1 || attack.max_range() > 1) {
attack_ss << attack.min_range() << "-" << attack.max_range() << ' ';
}
@ -654,7 +654,7 @@ std::string unit_topic_generator::operator()() const {
attack_ss.str(clear_stringstream);
// Damage type, with icon
const std::string type_icon = "icons/profiles/" + attack.type() + ".png";
const std::string type_icon = "icons/profiles/" + attack.type() + ".png~SCALE_INTO(16,16)";
push_tab_pair(row, lang_type, type_icon, padding);
// Show this attack's special, if it has any. Cross
@ -703,7 +703,7 @@ std::string unit_topic_generator::operator()() const {
push_header(first_res_row, _("Attack Type"));
push_header(first_res_row, _("Resistance"));
resistance_table.push_back(first_res_row);
utils::string_map dam_tab = movement_type.damage_table();
utils::string_map_res dam_tab = movement_type.damage_table();
for(std::pair<std::string, std::string> dam_it : dam_tab) {
std::vector<item> row;
int resistance = 100;
@ -716,8 +716,9 @@ std::string unit_topic_generator::operator()() const {
resist.replace(pos, 1, font::unicode_minus);
}
std::string color = unit_helper::resistance_color(resistance);
std::string lang_weapon = string_table["type_" + dam_it.first];
push_tab_pair(row, lang_weapon);
const std::string lang_type = string_table["type_" + dam_it.first];
const std::string type_icon = "icons/profiles/" + dam_it.first + ".png~SCALE_INTO(16,16)";
push_tab_pair(row, lang_type, type_icon, padding);
std::stringstream str;
str << "<format>color=\"" << color << "\" text='"<< resist << "'</format>";
const std::string markup = str.str();
@ -802,107 +803,114 @@ std::string unit_topic_generator::operator()() const {
std::string color = game_config::red_to_green(m.defense, false).to_hex_string();
std::stringstream str;
std::stringstream str_unformatted;
str << "<format>color='" << color << "' text='"<< m.defense << "%'</format>";
std::string markup = str.str();
str.str(clear_stringstream);
str << m.defense << "%";
row.emplace_back(markup, font::pango_line_width(str.str(), normal_font_size));
str_unformatted << m.defense << "%";
row.emplace_back(str.str(), font::pango_line_width(str_unformatted.str(), normal_font_size));
//movement - range: 1 .. 5, movetype::UNREACHABLE=impassable
str.str(clear_stringstream);
bool cannot_move = m.movement_cost > type_.movement();
if (cannot_move) { // cannot move in this terrain
color = "red";
} else if (m.movement_cost > 1) {
color = "yellow";
} else {
color = "white";
}
str << "<format>color=" << color << " text='";
str_unformatted.str(clear_stringstream);
const bool cannot_move = m.movement_cost > type_.movement(); // cannot move in this terrain
double movement_red_to_green = 100.0 - 25.0 * m.movement_cost;
// passing false to select the more saturated red-to-green scale
std::string movement_color = game_config::red_to_green(movement_red_to_green, false).to_hex_string();
str << "<format>color='" << movement_color << "' text='";
// A 5 MP margin; if the movement costs go above
// the unit's max moves + 5, we replace it with dashes.
if(cannot_move && (m.movement_cost > type_.movement() + 5)) {
str << font::unicode_figure_dash;
str_unformatted << font::unicode_figure_dash;
} else if(cannot_move) {
str_unformatted << "(" << m.movement_cost << ")";
} else {
str << m.movement_cost;
str_unformatted << m.movement_cost;
}
str << "'</format>";
markup = str.str();
str.str(clear_stringstream);
str << m.movement_cost;
row.emplace_back(markup, font::pango_line_width(str.str(), normal_font_size));
if(m.movement_cost != 0) {
const int movement_hexes_per_turn = type_.movement() / m.movement_cost;
str_unformatted << " ";
for(int i = 0; i < movement_hexes_per_turn; ++i) {
// Unicode horizontal black hexagon and Unicode zero width space (to allow a line break)
str_unformatted << "\u2b23\u200b";
}
}
str << str_unformatted.str() << "'</format>";
row.emplace_back(str.str(), font::pango_line_width(str_unformatted.str(), normal_font_size));
//defense cap
if (has_terrain_defense_caps) {
str.str(clear_stringstream);
str_unformatted.str(clear_stringstream);
if (m.defense_cap) {
str << "<format>color='"<< color <<"' text='" << m.defense << "%'</format>";
str_unformatted << m.defense << "%";
} else {
str << "<format>color=white text='" << font::unicode_figure_dash << "'</format>";
str_unformatted << font::unicode_figure_dash;
}
markup = str.str();
str.str(clear_stringstream);
if (m.defense_cap) {
str << m.defense << '%';
} else {
str << font::unicode_figure_dash;
}
row.emplace_back(markup, font::pango_line_width(str.str(), normal_font_size));
row.emplace_back(str.str(), font::pango_line_width(str_unformatted.str(), normal_font_size));
}
//vision
if (has_vision) {
str.str(clear_stringstream);
const bool cannot_view = m.vision_cost > type_.vision();
if (cannot_view) { // cannot view in this terrain
color = "red";
} else if (m.vision_cost > m.movement_cost) {
color = "yellow";
} else if (m.vision_cost == m.movement_cost) {
color = "white";
} else {
color = "green";
}
str << "<format>color=" << color << " text='";
str_unformatted.str(clear_stringstream);
const bool cannot_view = m.vision_cost > type_.vision(); // cannot view in this terrain
double vision_red_to_green = 100.0 - 25.0 * m.vision_cost;
// passing false to select the more saturated red-to-green scale
std::string vision_color = game_config::red_to_green(vision_red_to_green, false).to_hex_string();
str << "<format>color='" << vision_color << "' text='";
// A 5 MP margin; if the vision costs go above
// the unit's vision + 5, we replace it with dashes.
if(cannot_view && (m.vision_cost > type_.vision() + 5)) {
str << font::unicode_figure_dash;
str_unformatted << font::unicode_figure_dash;
} else if(cannot_view) {
str_unformatted << "(" << m.vision_cost << ")";
} else {
str << m.vision_cost;
str_unformatted << m.vision_cost;
}
str << "'</format>";
markup = str.str();
str.str(clear_stringstream);
str << m.vision_cost;
row.emplace_back(markup, font::pango_line_width(str.str(), normal_font_size));
if(m.vision_cost != 0) {
const int vision_hexes_per_turn = type_.vision() / m.vision_cost;
str_unformatted << " ";
for(int i = 0; i < vision_hexes_per_turn; ++i) {
// Unicode horizontal black hexagon and Unicode zero width space (to allow a line break)
str_unformatted << "\u2b23\u200b";
}
}
str << str_unformatted.str() << "'</format>";
row.emplace_back(str.str(), font::pango_line_width(str_unformatted.str(), normal_font_size));
}
//jamming
if (has_jamming) {
str.str(clear_stringstream);
const bool cannot_jam = m.jamming_cost > type_.jamming();
if (cannot_jam) { // cannot jamm in this terrain
color = "red";
} else if (m.jamming_cost > m.vision_cost) {
color = "yellow";
} else if (m.jamming_cost == m.vision_cost) {
color = "white";
} else {
color = "green";
}
str << "<format>color=" << color << " text='";
str_unformatted.str(clear_stringstream);
const bool cannot_jam = m.jamming_cost > type_.jamming(); // cannot jam in this terrain
double jamming_red_to_green = 100.0 - 25.0 * m.jamming_cost;
// passing false to select the more saturated red-to-green scale
std::string jamming_color = game_config::red_to_green(jamming_red_to_green, false).to_hex_string();
str << "<format>color='" << jamming_color << "' text='";
// A 5 MP margin; if the jamming costs go above
// the unit's jamming + 5, we replace it with dashes.
if (cannot_jam && m.jamming_cost > type_.jamming() + 5) {
str << font::unicode_figure_dash;
str_unformatted << font::unicode_figure_dash;
} else if(cannot_jam) {
str_unformatted << "(" << m.jamming_cost << ")";
} else {
str << m.jamming_cost;
str_unformatted << m.jamming_cost;
}
str << "'</format>";
push_tab_pair(row, str.str());
if(m.jamming_cost != 0) {
const int jamming_hexes_per_turn = type_.jamming() / m.jamming_cost;
str_unformatted << " ";
for(int i = 0; i < jamming_hexes_per_turn; ++i) {
// Unicode horizontal black hexagon and Unicode zero width space (to allow a line break)
str_unformatted << "\u2b23\u200b";
}
}
str << str_unformatted.str() << "'</format>";
row.emplace_back(str.str(), font::pango_line_width(str_unformatted.str(), normal_font_size));
}
table.push_back(row);

View file

@ -726,9 +726,9 @@ void movetype::terrain_defense::merge(const config & new_data, bool overwrite)
/**
* Returns a map from attack types to resistances.
*/
utils::string_map movetype::resistances::damage_table() const
utils::string_map_res movetype::resistances::damage_table() const
{
utils::string_map result;
utils::string_map_res result;
for (const config::attribute & attrb : cfg_.attribute_range()) {
result[attrb.first] = attrb.second;

View file

@ -226,7 +226,7 @@ public:
explicit resistances(const config & cfg) : cfg_(cfg) {}
/** Returns a map from attack types to resistances. */
utils::string_map damage_table() const;
utils::string_map_res damage_table() const;
/** Returns the resistance against the indicated attack. */
int resistance_against(const attack_type & attack) const;
/** Returns the resistance against the indicated damage type. */
@ -299,7 +299,7 @@ public:
int resistance_against(const std::string & damage_type) const
{ return resist_.resistance_against(damage_type); }
/** Returns a map from attack types to resistances. */
utils::string_map damage_table() const
utils::string_map_res damage_table() const
{ return resist_.damage_table(); }
/** Returns whether or not there are any terrain caps with respect to a set of terrains. */

View file

@ -539,6 +539,8 @@ static surface load_image_file(const image::locator& loc)
ERR_IMG << "could not open image '" << name << "'";
if(game_config::debug && name != game_config::images::missing)
return get_surface(game_config::images::missing, UNSCALED);
if(name != game_config::images::blank)
return get_surface(game_config::images::blank, UNSCALED);
}
return res;

View file

@ -468,11 +468,11 @@ static config unit_hp(reports::context& rc, const unit* u)
str << span_color(u->hp_color()) << u->hitpoints()
<< '/' << u->max_hitpoints() << naps;
std::set<std::string> resistances_table;
std::vector<std::string> resistances_table;
bool att_def_diff = false;
map_location displayed_unit_hex = rc.screen().displayed_unit_hex();
for (const utils::string_map::value_type &resist : u->get_base_resistances())
for (const utils::string_map_res::value_type &resist : u->get_base_resistances())
{
std::ostringstream line;
line << translation::gettext(resist.first.c_str()) << ": ";
@ -491,7 +491,7 @@ static config unit_hp(reports::context& rc, const unit* u)
<< naps << '\n';
att_def_diff = true;
}
resistances_table.insert(line.str());
resistances_table.push_back(line.str());
}
tooltip << _("Resistances: ");
@ -684,23 +684,29 @@ static config unit_moves(reports::context & rc, const unit* u, bool is_visible_u
for (const terrain_movement& tm : terrain_moves) {
tooltip << tm.name << ": ";
std::string color;
//movement - range: 1 .. 5, movetype::UNREACHABLE=impassable
const bool cannot_move = tm.moves > u->total_movement();
if (cannot_move) // cannot move in this terrain
color = "red";
else if (tm.moves > 1)
color = "yellow";
else
color = "white";
const bool cannot_move = tm.moves > u->total_movement(); // cannot move in this terrain
double movement_red_to_green = 100.0 - 25.0 * tm.moves;
// passing false to select the more saturated red-to-green scale
std::string color = game_config::red_to_green(movement_red_to_green, false).to_hex_string();
tooltip << "<span foreground=\"" << color << "\">";
// A 5 MP margin; if the movement costs go above
// the unit's max moves + 5, we replace it with dashes.
if (cannot_move && (tm.moves > u->total_movement() + 5)) {
tooltip << font::unicode_figure_dash;
} else if (cannot_move) {
tooltip << "(" << tm.moves << ")";
} else {
tooltip << tm.moves;
}
if(tm.moves != 0) {
const int movement_hexes_per_turn = u->total_movement() / tm.moves;
tooltip << " ";
for(int i = 0; i < movement_hexes_per_turn; ++i) {
tooltip << "\u2b23"; // Unicode horizontal black hexagon
}
}
tooltip << naps << '\n';
}
@ -741,12 +747,17 @@ REPORT_GENERATOR(selected_unit_moves, rc)
return unit_moves(rc, u, false);
}
/**
* Maps resistance <= -60 (resistance value <= -60%) to intense red.
* Maps resistance >= 60 (resistance value >= 60%) to intense green.
* Intermediate values are affinely mapped to the red-to-green scale,
* with 0 (0%) being mapped to yellow.
* Compare unit_helper::resistance_color().
*/
static inline const color_t attack_info_percent_color(int resistance)
{
// Compare unit_helper::resistance_color()
if (resistance < 0) return font::BAD_COLOR;
if (resistance > 0) return font::GOOD_COLOR;
return font::YELLOW_COLOR;
// Passing false to select the more saturated red-to-green scale.
return game_config::red_to_green(50.0 + resistance * 5.0 / 6.0, false);
}
static int attack_info(reports::context & rc, const attack_type &at, config &res, const unit &u, const map_location &hex, const unit* sec_u = nullptr, const_attack_ptr sec_u_weapon = nullptr)
@ -853,9 +864,9 @@ static int attack_info(reports::context & rc, const attack_type &at, config &res
std::string range = string_table["range_" + at.range()];
std::string lang_type = string_table["type_" + at.type()];
// SCALE_INTO_SHARP() is needed in case the 72x72 images/misc/missing-image.png is substituted.
const std::string range_png = std::string("icons/profiles/") + at.range() + "_attack.png~SCALE_INTO_SHARP(16,16)";
const std::string type_png = std::string("icons/profiles/") + at.type() + ".png~SCALE_INTO_SHARP(16,16)";
// SCALE_INTO() is needed in case the 72x72 images/misc/missing-image.png is substituted.
const std::string range_png = std::string("icons/profiles/") + at.range() + "_attack.png~SCALE_INTO(16,16)";
const std::string type_png = std::string("icons/profiles/") + at.type() + ".png~SCALE_INTO(16,16)";
const bool range_png_exists = image::locator(range_png).file_exists();
const bool type_png_exists = image::locator(type_png).file_exists();
@ -914,8 +925,8 @@ static int attack_info(reports::context & rc, const attack_type &at, config &res
// The icons are 16x16. We add 5px padding for alignment reasons (placement of the icon in relation to ascender and descender letters).
const std::string spacer = "misc/blank.png~CROP(0, 0, 16, 21)"; // 21 == 16+5
if (range_png_exists) add_image(res, spacer + "~BLIT(" + range_png + ",0,5)", damage_versus.tooltip);
if (type_png_exists) add_image(res, spacer + "~BLIT(" + type_png + ",0,5)", damage_versus.tooltip);
add_image(res, spacer + "~BLIT(" + range_png + ",0,5)", damage_versus.tooltip);
add_image(res, spacer + "~BLIT(" + type_png + ",0,5)", damage_versus.tooltip);
add_text(res, damage_and_num_attacks.str, damage_and_num_attacks.tooltip);
add_text(res, damage_versus.str, damage_versus.tooltip); // This string is usually empty

View file

@ -35,6 +35,23 @@ namespace utils {
using string_map = std::map<std::string, t_string>;
const std::vector<std::string> res_order = {"blade", "pierce", "impact", "fire", "cold", "arcane"};
struct res_compare {
/** Returns whether a < b, considering res_order. */
bool operator()(const std::string& a, const std::string& b) const {
for(const std::string& r : res_order) {
if (b == r) // this means b <= a, so a < b is false
return false;
if (a == r)
return true;
}
return a < b; // fallback only reached when neither a nor b occur in res_order
}
};
using string_map_res = std::map<std::string, t_string, res_compare>;
bool isnewline(const char c);
bool portable_isspace(const char c);
bool notspace(char c);

View file

@ -40,18 +40,17 @@ bool will_certainly_advance(const unit_map::iterator &u)
return u->advances() && number_of_possible_advances(*u) > 0;
}
/**
* Maps resistance <= -60 (resistance value <= -60%) to intense red.
* Maps resistance >= 60 (resistance value >= 60%) to intense green.
* Intermediate values are affinely mapped to the red-to-green scale,
* with 0 (0%) being mapped to yellow.
* Compare attack_info_percent_color() in reports.cpp.
*/
std::string resistance_color(const int resistance)
{
if (resistance < 0)
return std::string("#FF0000");
if (resistance <= 20)
return std::string("#FFFF00");
if (resistance <= 40)
return std::string("#FFFFFF");
return std::string("#00FF00");
// Passing false to select the more saturated red-to-green scale.
return game_config::red_to_green(50.0 + resistance * 5.0 / 6.0, false).to_hex_string();
}
static std::string unit_level_tooltip(const int level, const std::vector<std::string> &adv_to_types, const std::vector<config> &adv_to_mods)

View file

@ -1034,7 +1034,7 @@ public:
}
/** Gets resistances without any abilities applied. */
utils::string_map get_base_resistances() const
utils::string_map_res get_base_resistances() const
{
return movement_type_.damage_table();
}