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:
parent
be62da89b1
commit
5f2d71b016
5 changed files with 160 additions and 170 deletions
203
src/actions.cpp
203
src/actions.cpp
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue