At first it was supposed to be a small patch...

to fix weapons not being translated in the attack box. But now it also
handles the way the battle statistics are stored in memory: the
optional part is now strictly user-oriented, and hence can be fully
translated. As a consequence, the common part is much smaller, almost
a POD; it should speed up the AI since both the memory footprint and
the _struction time are notably reduced. To avoid changing too much
code, there are still two strings in the common structure in order to
handle attack specials; they could be replaced later on by an integer
bitset to further speed up the game.
This commit is contained in:
Guillaume Melquiond 2004-12-05 18:37:36 +00:00
parent be62da89b1
commit 5f2d71b016
5 changed files with 160 additions and 170 deletions

View file

@ -32,6 +32,7 @@
#include "statistics.hpp"
#include "unit_display.hpp"
#include "util.hpp"
#include "widgets/menu.hpp"
#include <cmath>
#include <set>
@ -184,15 +185,14 @@ gamemap::location under_leadership(const std::map<gamemap::location,unit>& units
return best_loc;
}
battle_stats evaluate_battle_stats(
const gamemap& map,
const gamemap::location& attacker,
const gamemap::location& defender,
int attack_with,
std::map<gamemap::location,unit>& units,
const gamestatus& state,
gamemap::TERRAIN attacker_terrain_override,
bool include_strings)
battle_stats evaluate_battle_stats(const gamemap& map,
const gamemap::location& attacker,
const gamemap::location& defender,
int attack_with,
std::map<gamemap::location,unit>& units,
const gamestatus& state,
gamemap::TERRAIN attacker_terrain_override,
battle_stats_strings *strings)
{
//if these are both genuine positions, work out the range
//combat is taking place at
@ -202,8 +202,8 @@ battle_stats evaluate_battle_stats(
res.attack_with = attack_with;
if(include_strings)
res.defend_name = _("none");
if (strings)
strings->defend_name = _("none");
const std::map<gamemap::location,unit>::iterator a = units.find(attacker);
const std::map<gamemap::location,unit>::iterator d = units.find(defender);
@ -221,23 +221,24 @@ battle_stats evaluate_battle_stats(
const std::vector<attack_type>& attacker_attacks = a->second.attacks();
const std::vector<attack_type>& defender_attacks = d->second.attacks();
assert(attack_with >= 0 && attack_with < int(attacker_attacks.size()));
assert((unsigned)attack_with < attacker_attacks.size());
const attack_type& attack = attacker_attacks[attack_with];
res.attacker_special = attack.special();
static const std::string charge_string("charge");
const bool charge = (attack.special() == charge_string);
const bool charge = res.attacker_special == charge_string;
const bool steadfast = d->second.type().steadfast();
bool backstab = false;
static const std::string to_the_death_string("berserk");
res.to_the_death = attack.special() == to_the_death_string;
res.to_the_death = res.attacker_special == to_the_death_string;
res.defender_strikes_first = false;
static const std::string backstab_string("backstab");
if(attack.special() == backstab_string) {
if (res.attacker_special == backstab_string) {
gamemap::location adj[6];
get_adjacent_tiles(defender,adj);
get_adjacent_tiles(defender, adj);
int i;
for(i = 0; i != 6; ++i) {
if(adj[i] == attacker)
@ -254,26 +255,24 @@ battle_stats evaluate_battle_stats(
}
static const std::string plague_string("plague");
res.attacker_plague = !d->second.type().not_living() && (attack.special() == plague_string)
res.attacker_plague = !d->second.type().not_living() && (res.attacker_special == plague_string)
&& !map.is_village(defender);
res.defender_plague = false;
res.attack_name = attack.name();
res.attack_type = attack.type();
static const std::string slow_string("slow");
res.attacker_slows = attack.special() == slow_string;
res.attacker_slows = res.attacker_special == slow_string;
if(include_strings) {
res.attack_special = attack.special();
res.attack_icon = attack.icon();
if (strings) {
strings->attack_name = egettext(attack.name().c_str());
strings->attack_type = egettext(attack.type().c_str());
strings->attack_special = egettext(res.attacker_special.c_str());
strings->attack_icon = attack.icon();
//don't show backstabbing unless it's actually happening
if(res.attack_special == "backstab" && !backstab)
res.attack_special = "";
if(attack.special() == backstab_string && !backstab)
strings->attack_special.clear();
res.range = (attack.range() == attack_type::SHORT_RANGE ?
"Melee" : "Ranged");
strings->range = gettext(attack.range() == attack_type::SHORT_RANGE ? N_("melee") : N_("ranged"));
}
res.nattacks = attack.num_attacks();
@ -299,16 +298,18 @@ battle_stats evaluate_battle_stats(
static const std::string drain_string("drain");
static const std::string magical_string("magical");
static const std::string EMPTY_COLUMN = std::string(1, COLUMN_SEPARATOR) + ' ' + COLUMN_SEPARATOR;
res.damage_attacker_takes = 0;
if(counterattack) {
if (counterattack) {
const attack_type& defend = defender_attacks[defend_with];
if(defend.special() == to_the_death_string) {
res.defender_special = defend.special();
if (res.defender_special == to_the_death_string) {
res.to_the_death = true;
}
//magical attacks always have a 70% chance to hit
if(defend.special() == magical_string) {
if (res.defender_special == magical_string) {
res.chance_to_hit_attacker = 70;
}
@ -319,53 +320,51 @@ battle_stats evaluate_battle_stats(
//res.damage_attacker_takes = (base_damage * (100+modifier))/100;
if(include_strings) {
if (strings) {
std::stringstream str_base;
str_base << _("base damage") << "," << base_damage << ",";
res.defend_calculations.push_back(str_base.str());
str_base << _("base damage") << COLUMN_SEPARATOR << base_damage;
strings->defend_calculations.push_back(str_base.str());
}
const int resist = resistance_modifier - 100;
percent += resist;
if(include_strings && resist != 0) {
if (strings && resist != 0) {
std::stringstream str_resist;
str_resist << gettext(resist < 0 ? N_("attacker resistance vs") : N_("attacker vulnerability vs"))
<< " " << gettext(defend.type().c_str())
<< ", ,^" << (resist > 0 ? "+" : "") << resist << "%";
res.defend_calculations.push_back(str_resist.str());
<< ' ' << gettext(defend.type().c_str()) << EMPTY_COLUMN
<< (resist > 0 ? "+" : "") << resist << '%';
strings->defend_calculations.push_back(str_resist.str());
}
const int tod_modifier = combat_modifier(state,units,d->first,d->second.type().alignment());
percent += tod_modifier;
if(include_strings && tod_modifier != 0) {
if (strings && tod_modifier != 0) {
std::stringstream str_mod;
const time_of_day& tod = timeofday_at(state,units,d->first);
str_mod << tod.name << ", ,^"
<< (tod_modifier > 0 ? "+" : "") << tod_modifier << "%";
res.defend_calculations.push_back(str_mod.str());
str_mod << tod.name << EMPTY_COLUMN << (tod_modifier > 0 ? "+" : "") << tod_modifier << '%';
strings->defend_calculations.push_back(str_mod.str());
}
int leader_bonus = 0;
if(under_leadership(units,defender,&leader_bonus).valid()) {
if (under_leadership(units, defender, &leader_bonus).valid()) {
percent += leader_bonus;
if(include_strings) {
if (strings) {
std::stringstream str;
str << _("leadership") << ", ,^+" + str_cast(leader_bonus) + "%";
res.defend_calculations.push_back(str.str());
str << _("leadership") << EMPTY_COLUMN << '+' << leader_bonus << '%';
strings->defend_calculations.push_back(str.str());
}
}
if(charge) {
if (charge) {
percent = (100+percent)*2 - 100;
if(include_strings) {
if (strings) {
std::stringstream str;
str << _("charge") << ", ," << _("Doubled");
res.defend_calculations.push_back(str.str());
str << _("charge") << EMPTY_COLUMN << _("Doubled");
strings->defend_calculations.push_back(str.str());
}
}
@ -388,48 +387,48 @@ battle_stats evaluate_battle_stats(
res.damage_attacker_takes = maximum<int>(1,base_damage + difference);
if(include_strings) {
if (strings) {
percent *= is_negative;
std::stringstream str;
str << _("total damage") << "," << res.damage_attacker_takes
<< ",^" << (percent >= 0 ? "+" : "") << percent << "% (" << (difference >= 0 ? "+" : "") << difference << ")";
res.defend_calculations.push_back(str.str());
str << _("total damage") << COLUMN_SEPARATOR << res.damage_attacker_takes
<< COLUMN_SEPARATOR << (percent >= 0 ? "+" : "") << percent
<< "% (" << (difference >= 0 ? "+" : "") << difference << ')';
strings->defend_calculations.push_back(str.str());
}
res.ndefends = defend.num_attacks();
res.defend_name = defend.name();
res.defend_type = defend.type();
if(include_strings) {
res.defend_special = defend.special();
res.defend_icon = defend.icon();
if (strings) {
strings->defend_name = egettext(defend.name().c_str());
strings->defend_type = egettext(defend.type().c_str());
strings->defend_special = egettext(res.defender_special.c_str());
strings->defend_icon = defend.icon();
}
//if the defender drains, and the attacker is a living creature, then
//the defender will drain for half the damage it does
if(defend.special() == drain_string && !a->second.type().not_living()) {
if (res.defender_special == drain_string && !a->second.type().not_living()) {
res.amount_defender_drains = res.damage_attacker_takes/2;
} else {
res.amount_defender_drains = 0;
}
res.defender_plague = !a->second.type().not_living() && (defend.special() == plague_string)
res.defender_plague = !a->second.type().not_living() && (res.defender_special == plague_string)
&& !map.is_village(attacker);
res.defender_slows = (defend.special() == slow_string);
static const std::string first_strike = "firststrike";
res.defender_strikes_first = defend.special() == first_strike && attack.special() != first_strike;
res.defender_strikes_first = res.defender_special == first_strike && res.attacker_special != first_strike;
}
if(attack.special() == magical_string)
if (res.attacker_special == magical_string)
res.chance_to_hit_defender = 70;
static const std::string marksman_string("marksman");
//offensive marksman attacks always have at least 60% chance to hit
if(res.chance_to_hit_defender < 60 && attack.special() == marksman_string)
if(res.chance_to_hit_defender < 60 && res.attacker_special == marksman_string)
res.chance_to_hit_defender = 60;
int percent = 0;
@ -437,73 +436,70 @@ battle_stats evaluate_battle_stats(
const int base_damage = attack.damage();
const int resistance_modifier = d->second.damage_against(attack);
if(include_strings) {
if (strings) {
std::stringstream str_base;
str_base << _("base damage") << "," << base_damage << ",";
res.attack_calculations.push_back(str_base.str());
str_base << _("base damage") << COLUMN_SEPARATOR << base_damage << COLUMN_SEPARATOR;
strings->attack_calculations.push_back(str_base.str());
}
const int resist = resistance_modifier - 100;
percent += resist;
if(include_strings && resist != 0) {
if (strings && resist != 0) {
std::stringstream str_resist;
str_resist << gettext(resist < 0 ? N_("defender resistance vs") : N_("defender vulnerability vs"))
<< " " << gettext(attack.type().c_str())
<< ", ,^" << (resist > 0 ? "+" : "") << resist << "%";
res.attack_calculations.push_back(str_resist.str());
<< ' ' << gettext(attack.type().c_str()) << EMPTY_COLUMN
<< (resist > 0 ? "+" : "") << resist << '%';
strings->attack_calculations.push_back(str_resist.str());
}
const int tod_modifier = combat_modifier(state,units,a->first,a->second.type().alignment());
percent += tod_modifier;
if(include_strings && tod_modifier != 0) {
if (strings && tod_modifier != 0) {
std::stringstream str_mod;
const time_of_day& tod = timeofday_at(state,units,a->first);
str_mod << tod.name << ", ,^"
<< (tod_modifier > 0 ? "+" : "") << tod_modifier << "%";
res.attack_calculations.push_back(str_mod.str());
str_mod << tod.name << EMPTY_COLUMN << (tod_modifier > 0 ? "+" : "") << tod_modifier << '%';
strings->attack_calculations.push_back(str_mod.str());
}
int leader_bonus = 0;
if(under_leadership(units,attacker,&leader_bonus).valid()) {
if (under_leadership(units,attacker,&leader_bonus).valid()) {
percent += leader_bonus;
if(include_strings) {
if(strings) {
std::stringstream str;
str << _("leadership") << ", ,^+" + str_cast(leader_bonus) + "%";
res.attack_calculations.push_back(str.str());
str << _("leadership") << EMPTY_COLUMN << '+' << leader_bonus << '%';
strings->attack_calculations.push_back(str.str());
}
}
if(charge) {
if (charge) {
percent = (100+percent)*2 - 100;
if(include_strings) {
if (strings) {
std::stringstream str;
str << _("charge") << ", ," << _("Doubled");
res.attack_calculations.push_back(str.str());
str << _("charge") << EMPTY_COLUMN << _("Doubled");
strings->attack_calculations.push_back(str.str());
}
}
if(backstab) {
if (backstab) {
percent = (100+percent)*2 - 100;
if(include_strings) {
if (strings) {
std::stringstream str;
str << _("backstab") << ", ," << _("Doubled");
res.attack_calculations.push_back(str.str());
str << _("backstab") << EMPTY_COLUMN << _("Doubled");
strings->attack_calculations.push_back(str.str());
}
}
if(steadfast) {
if (steadfast) {
percent = (100+percent)/2 - 100;
if(include_strings) {
if (strings) {
std::stringstream str;
str << _("steadfast") << ", ," << _("Halved");
res.attack_calculations.push_back(str.str());
str << _("steadfast") << EMPTY_COLUMN << _("Halved");
strings->attack_calculations.push_back(str.str());
}
}
@ -525,13 +521,14 @@ battle_stats evaluate_battle_stats(
difference /= 100*is_negative;
res.damage_defender_takes = maximum<int>(1,base_damage + difference);
if(include_strings) {
if (strings) {
percent *= is_negative;
std::stringstream str;
str << _("total damage") << "," << res.damage_defender_takes
<< ",^" << (percent >= 0 ? "+" : "") << percent << "% (" << (difference >= 0 ? "+" : "") << difference << ")";
res.attack_calculations.push_back(str.str());
str << _("total damage") << COLUMN_SEPARATOR << res.damage_defender_takes
<< COLUMN_SEPARATOR << (percent >= 0 ? "+" : "") << percent
<< "% (" << (difference >= 0 ? "+" : "") << difference << ')';
strings->attack_calculations.push_back(str.str());
}
//if the attacker drains, and the defender is a living creature, then
@ -721,7 +718,7 @@ void attack(display& gui, const gamemap& map,
gui.update_display();
break;
} else if(hits) {
if(stats.attack_special == poison_string &&
if (stats.attacker_special == poison_string &&
d->second.has_flag("poisoned") == false &&
!d->second.type().not_living()) {
gui.float_label(d->first,_("poisoned"),255,0,0);
@ -737,7 +734,7 @@ void attack(display& gui, const gamemap& map,
//if the defender is turned to stone, the fight stops immediately
static const std::string stone_string("stone");
if(stats.attack_special == stone_string) {
if (stats.attacker_special == stone_string) {
gui.float_label(d->first,_("stone"),255,0,0);
d->second.set_flag(stone_string);
stats.ndefends = 0;
@ -867,7 +864,7 @@ void attack(display& gui, const gamemap& map,
recalculate_fog(map,state,info,units,teams,attacker_side-1);
break;
} else if(hits) {
if(stats.defend_special == poison_string &&
if (stats.defender_special == poison_string &&
a->second.has_flag("poisoned") == false &&
!a->second.type().not_living()) {
gui.float_label(a->first,_("poisoned"),255,0,0);
@ -884,7 +881,7 @@ void attack(display& gui, const gamemap& map,
//if the attacker is turned to stone, the fight stops immediately
static const std::string stone_string("stone");
if(stats.defend_special == stone_string) {
if (stats.defender_special == stone_string) {
gui.float_label(a->first,_("stone"),255,0,0);
a->second.set_flag(stone_string);
stats.ndefends = 0;

View file

@ -47,11 +47,6 @@ std::string recruit_unit(const gamemap& map, int team, unit_map& units,
//battle that could take place.
struct battle_stats
{
std::string attack_name, defend_name;
std::string attack_type, defend_type;
std::string attack_special, defend_special;
std::string range;
std::string attack_icon, defend_icon;
int chance_to_hit_attacker, chance_to_hit_defender;
int damage_attacker_takes, damage_defender_takes;
int amount_attacker_drains, amount_defender_drains;
@ -59,8 +54,18 @@ struct battle_stats
int attack_with, defend_with;
bool attacker_plague, defender_plague;
bool attacker_slows, defender_slows;
std::vector<std::string> attack_calculations, defend_calculations;
bool to_the_death, defender_strikes_first;
std::string attacker_special, defender_special;
};
struct battle_stats_strings
{
std::string attack_name, defend_name;
std::string attack_type, defend_type;
std::string attack_special, defend_special;
std::string range;
std::string attack_icon, defend_icon;
std::vector<std::string> attack_calculations, defend_calculations;
};
//evaluate_battle_stats: a function which, if given an attacker
@ -76,15 +81,14 @@ struct battle_stats
//if include_strings is false, then none of the strings in
//battle_stats will be populated, and the function will run
//substantially faster.
battle_stats evaluate_battle_stats(
const gamemap& map,
const gamemap::location& attacker,
const gamemap::location& defender,
int attack_with,
std::map<gamemap::location,unit>& units,
const gamestatus& state,
gamemap::TERRAIN attacker_terrain_override=0,
bool include_strings=true);
battle_stats evaluate_battle_stats(const gamemap& map,
const gamemap::location& attacker,
const gamemap::location& defender,
int attack_with,
std::map<gamemap::location,unit>& units,
const gamestatus& state,
gamemap::TERRAIN attacker_terrain_override = 0,
battle_stats_strings *strings = NULL);
//attack: executes an attack.
void attack(display& gui, const gamemap& map,

View file

@ -110,7 +110,7 @@ private:
int best_attack_rating = -1;
int best_attack = -1;
for(size_t n = 0; n != attacks.size(); ++n) {
const battle_stats stats = evaluate_battle_stats(get_info().map, attacker, defender, n, get_info().units, get_info().state, 0, false);
const battle_stats stats = evaluate_battle_stats(get_info().map, attacker, defender, n, get_info().units, get_info().state);
const int attack_rating = stats.damage_defender_takes*stats.nattacks*stats.chance_to_hit_defender;
if(best_attack == -1 || attack_rating > best_attack_rating) {
best_attack = n;

View file

@ -282,7 +282,7 @@ int ai::choose_weapon(const location& att, const location& def,
for(size_t a = 0; a != attacks.size(); ++a) {
const battle_stats stats = evaluate_battle_stats(map_, att, def, a, units_,
state_, terrain, false);
state_, terrain);
//TODO: improve this rating formula!
const double rating =

View file

@ -478,58 +478,59 @@ namespace {
class attack_calculations_displayer : public gui::dialog_button_action
{
public:
attack_calculations_displayer(display& disp, std::vector<battle_stats>& stats)
typedef std::vector< battle_stats_strings > stats_vector;
attack_calculations_displayer(display &disp, stats_vector const &stats)
: disp_(disp), stats_(stats)
{}
RESULT button_pressed(int selection);
private:
display& disp_;
std::vector<battle_stats>& stats_;
display &disp_;
stats_vector const &stats_;
};
gui::dialog_button_action::RESULT attack_calculations_displayer::button_pressed(int selection)
{
const size_t index = size_t(selection);
if(index < stats_.size()) {
const battle_stats& stats = stats_[index];
std::vector<std::string> calcs;
battle_stats_strings const &sts = stats_[index];
std::vector< std::string > sts_att = sts.attack_calculations,
sts_def = sts.defend_calculations,
calcs;
unsigned sts_att_sz = sts_att.size(),
sts_def_sz = sts_def.size(),
sts_sz = maximum< unsigned >(sts_att_sz, sts_def_sz);
std::stringstream str;
str << _("Attacker") << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR;
if(stats.defend_calculations.empty() == false) {
if (sts_def_sz > 0)
str << _("Defender");
}
calcs.push_back(str.str());
for(size_t i = 0; i < maximum<size_t>(stats.attack_calculations.size(),stats.defend_calculations.size()); ++i) {
for(unsigned i = 0; i < sts_sz; ++i) {
std::stringstream str;
if(i < stats.attack_calculations.size() && stats.attack_calculations.empty() == false) {
str << stats.attack_calculations[i];
} else {
if (i < sts_att_sz)
str << sts_att[i];
else
str << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR << ' ';
}
str << COLUMN_SEPARATOR;
if(i < stats.defend_calculations.size() && stats.defend_calculations.empty() == false) {
str << stats.defend_calculations[i];
} else {
if (i < sts_def_sz)
str << sts_def[i];
else
str << ' ' << COLUMN_SEPARATOR << ' ' << COLUMN_SEPARATOR << ' ';
}
calcs.push_back(str.str());
}
gui::show_dialog(disp_,NULL,"",_("Damage Calculations"),gui::OK_ONLY,&calcs);
gui::show_dialog(disp_, NULL, "", _("Damage Calculations"), gui::OK_ONLY, &calcs);
}
return NO_EFFECT;
}
}
bool turn_info::attack_enemy(unit_map::iterator attacker, unit_map::iterator defender)
{
//we must get locations by value instead of by references, because the iterators
@ -546,7 +547,7 @@ bool turn_info::attack_enemy(unit_map::iterator attacker, unit_map::iterator def
int best_weapon_index = -1;
int best_weapon_rating = 0;
std::vector<battle_stats> stats;
attack_calculations_displayer::stats_vector stats;
for(size_t a = 0; a != attacks.size(); ++a) {
if(attacks[a].hexes() < range)
@ -554,52 +555,40 @@ bool turn_info::attack_enemy(unit_map::iterator attacker, unit_map::iterator def
attacks_in_range.push_back(a);
stats.push_back(evaluate_battle_stats(map_, attacker_loc, defender_loc,
a, units_, status_));
battle_stats_strings sts;
battle_stats st = evaluate_battle_stats(map_, attacker_loc, defender_loc,
a, units_, status_, 0, &sts);
stats.push_back(sts);
int weapon_rating = stats.back().chance_to_hit_defender *
stats.back().damage_defender_takes * stats.back().nattacks;
int weapon_rating = st.chance_to_hit_defender * st.damage_defender_takes * st.nattacks;
if (best_weapon_index < 0 || best_weapon_rating < weapon_rating) {
best_weapon_index = items.size();
best_weapon_rating = weapon_rating;
}
const battle_stats& st = stats.back();
const std::string& attack_name = st.attack_name;
const std::string& attack_special = st.attack_special.empty() ? "" : gettext(st.attack_special.c_str());
const std::string& defend_name = st.defend_name;
const std::string& defend_special = st.defend_special.empty() ? "" : gettext(st.defend_special.c_str());
const std::string& range = gettext(st.range == "Melee" ? N_("melee") : N_("ranged"));
//if there is an attack special or defend special, we output a single space for the other unit, to make sure
//that the attacks line up nicely.
std::string special_pad = (attack_special.empty() == false || defend_special.empty() == false) ? " " : "";
std::string special_pad = (sts.attack_special.empty() && sts.defend_special.empty()) ? "" : " ";
std::stringstream att;
att << IMAGE_PREFIX << stats.back().attack_icon << COLUMN_SEPARATOR
<< font::BOLD_TEXT << attack_name
<< "\n" << stats.back().damage_defender_takes << "-"
<< stats.back().nattacks << " " << range << " ("
<< stats.back().chance_to_hit_defender << "%)\n"
<< attack_special << special_pad;
att << COLUMN_SEPARATOR << _("vs") << COLUMN_SEPARATOR;
att << font::BOLD_TEXT << defend_name << "\n" << stats.back().damage_attacker_takes << "-"
<< stats.back().ndefends << " " << range << " ("
<< stats.back().chance_to_hit_attacker
<< "%)\n" << defend_special << special_pad << COLUMN_SEPARATOR
<< IMAGE_PREFIX << stats.back().defend_icon;
att << IMAGE_PREFIX << sts.attack_icon << COLUMN_SEPARATOR
<< font::BOLD_TEXT << sts.attack_name << "\n" << st.damage_defender_takes << "-"
<< st.nattacks << " " << sts.range << " (" << st.chance_to_hit_defender << "%)\n"
<< sts.attack_special << special_pad
<< COLUMN_SEPARATOR << _("vs") << COLUMN_SEPARATOR
<< font::BOLD_TEXT << sts.defend_name << "\n" << st.damage_attacker_takes << "-"
<< st.ndefends << " " << sts.range << " (" << st.chance_to_hit_attacker << "%)\n"
<< sts.defend_special << special_pad << COLUMN_SEPARATOR
<< IMAGE_PREFIX << sts.defend_icon;
items.push_back(att.str());
}
if (best_weapon_index >= 0) {
items[best_weapon_index] = "*" + items[best_weapon_index];
items[best_weapon_index] = DEFAULT_ITEM + items[best_weapon_index];
}
//make it so that when we attack an enemy, the attacking unit
//is again shown in the status bar, so that we can easily
//compare between the attacking and defending unit