New function best_attack_weapon:
uses (slightly modified) heuristic to figure out what the best weapon to use is. Make mouse_events use it to choose default attack to highlight. Plan is for ai to use this as well.
This commit is contained in:
parent
a8001b7bda
commit
95727ed284
3 changed files with 91 additions and 68 deletions
|
@ -730,10 +730,11 @@ battle_stats evaluate_battle_stats(const gamemap& map,
|
|||
battle_context::battle_context(const gamemap& map, const std::vector<team>& teams, const std::map<gamemap::location,unit>& units,
|
||||
const gamestatus& status, const game_data& gamedata,
|
||||
const gamemap::location& attacker_loc, const gamemap::location& defender_loc,
|
||||
const attack_type& attacker_weapon): attacker_stats_(NULL), defender_stats_(NULL)
|
||||
unsigned int attacker_weapon): attacker_stats_(NULL), defender_stats_(NULL)
|
||||
{
|
||||
const unit& attacker = units.find(attacker_loc)->second;
|
||||
const unit& defender = units.find(defender_loc)->second;
|
||||
const attack_type &att = attacker.attacks()[attacker_weapon];
|
||||
|
||||
// To choose the best defender weapon, we have to compare different
|
||||
// weapons with an heuristic function. The heuristic function requires
|
||||
|
@ -743,21 +744,21 @@ battle_context::battle_context(const gamemap& map, const std::vector<team>& team
|
|||
// has the best rating.
|
||||
int best_rating = -1;
|
||||
|
||||
std::vector<attack_type>::const_iterator i;
|
||||
for (i = defender.attacks().begin(); i != defender.attacks().end(); i++) {
|
||||
for (unsigned int i = 0; i < defender.attacks().size(); i++) {
|
||||
const attack_type &def = defender.attacks()[i];
|
||||
// Skip weapons that do not match the attacker weapon range.
|
||||
if (i->range() != attacker_weapon.range())
|
||||
if (def.range() != att.range())
|
||||
continue;
|
||||
|
||||
// Skip weapons that have a null defense weight.
|
||||
if (i->defense_weight() <= 0)
|
||||
if (def.defense_weight() <= 0)
|
||||
continue;
|
||||
|
||||
unit_stats *current_attacker = new unit_stats(attacker, attacker_loc, &attacker_weapon, true,
|
||||
defender, defender_loc, &(*i),
|
||||
unit_stats *current_attacker = new unit_stats(attacker, attacker_loc, attacker_weapon,
|
||||
true, defender, defender_loc, &def,
|
||||
units, teams, status, map, gamedata);
|
||||
unit_stats *current_defender = new unit_stats(defender, defender_loc, &(*i), false,
|
||||
attacker, attacker_loc, &attacker_weapon,
|
||||
unit_stats *current_defender = new unit_stats(defender, defender_loc, i, false,
|
||||
attacker, attacker_loc, &att,
|
||||
units, teams, status, map, gamedata);
|
||||
int current_rating = rate_defender_weapon(*current_attacker, *current_defender);
|
||||
assert(current_rating >= 0);
|
||||
|
@ -777,11 +778,11 @@ battle_context::battle_context(const gamemap& map, const std::vector<team>& team
|
|||
// If there is no defender weapon, compute the stats without a defender weapon.
|
||||
if (attacker_stats_ == NULL) {
|
||||
wassert(defender_stats_ == NULL);
|
||||
attacker_stats_ = new unit_stats(attacker, attacker_loc, &attacker_weapon, true,
|
||||
attacker_stats_ = new unit_stats(attacker, attacker_loc, attacker_weapon, true,
|
||||
defender, defender_loc, NULL,
|
||||
units, teams, status, map, gamedata);
|
||||
defender_stats_ = new unit_stats(defender, defender_loc, NULL, false,
|
||||
attacker, attacker_loc, &attacker_weapon,
|
||||
defender_stats_ = new unit_stats(defender, defender_loc, -1, false,
|
||||
attacker, attacker_loc, &att,
|
||||
units, teams, status, map, gamedata);
|
||||
}
|
||||
}
|
||||
|
@ -804,7 +805,7 @@ battle_context& battle_context::operator=(const battle_context &other)
|
|||
}
|
||||
|
||||
battle_context::unit_stats::unit_stats(const unit &u, const gamemap::location& u_loc,
|
||||
const attack_type *u_weapon, bool attacking,
|
||||
int u_attack_num, bool attacking,
|
||||
const unit &opp, const gamemap::location& opp_loc,
|
||||
const attack_type *opp_weapon,
|
||||
const std::map<gamemap::location,unit>& units,
|
||||
|
@ -814,7 +815,12 @@ battle_context::unit_stats::unit_stats(const unit &u, const gamemap::location& u
|
|||
const game_data& gamedata)
|
||||
{
|
||||
// Get the current state of the unit.
|
||||
weapon = u_weapon;
|
||||
attack_num = u_attack_num;
|
||||
if (attack_num >= 0) {
|
||||
weapon = &u.attacks()[attack_num];
|
||||
} else {
|
||||
weapon = NULL;
|
||||
}
|
||||
is_attacker = attacking;
|
||||
is_poisoned = utils::string_bool(u.get_state("poisoned"));
|
||||
is_slowed = utils::string_bool(u.get_state("slowed"));
|
||||
|
@ -950,6 +956,9 @@ unsigned int battle_context::rate_attacker_weapon(double attack_weight) const
|
|||
if (defender_stats_->drains)
|
||||
attack_weight /= 2;
|
||||
|
||||
// Bias towards more damaging attacks.
|
||||
attack_weight += attacker_stats_->num_blows * attacker_stats_->damage;
|
||||
|
||||
attack_weight *= attacker_stats_->num_blows * attacker_stats_->damage;
|
||||
if (defender_stats_->num_blows * defender_stats_->damage)
|
||||
attack_weight /= defender_stats_->num_blows * defender_stats_->damage;
|
||||
|
@ -965,6 +974,32 @@ int battle_context::rate_defender_weapon(const unit_stats&, const unit_stats& d_
|
|||
return (int) (d_stats.damage * d_stats.num_blows * d_stats.weapon->defense_weight());
|
||||
}
|
||||
|
||||
int best_attack_weapon(const gamemap& map, const std::vector<team>& teams,
|
||||
const std::map<gamemap::location,unit>& units,
|
||||
const gamestatus& status, const game_data& gamedata,
|
||||
const gamemap::location& attacker_loc,
|
||||
const gamemap::location& defender_loc,
|
||||
std::vector<battle_context> &bc_vector)
|
||||
{
|
||||
int best_weapon_index = -1;
|
||||
unsigned int best_weapon_rating;
|
||||
const std::vector<attack_type>& attacks = units.find(attacker_loc)->second.attacks();
|
||||
|
||||
for (unsigned int i = 0; i != attacks.size(); ++i) {
|
||||
// skip weapons with attack_weight=0
|
||||
if (attacks[i].attack_weight() > 0) {
|
||||
battle_context bc(map, teams, units, status, gamedata, attacker_loc, defender_loc, i);
|
||||
bc_vector.push_back(bc);
|
||||
unsigned int weapon_rating = bc.rate_attacker_weapon(attacks[i].attack_weight());
|
||||
|
||||
if (best_weapon_index < 0 || best_weapon_rating < weapon_rating) {
|
||||
best_weapon_index = i;
|
||||
best_weapon_rating = weapon_rating;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best_weapon_index;
|
||||
}
|
||||
|
||||
static std::string unit_dump(std::pair< gamemap::location, unit > const &u)
|
||||
{
|
||||
|
@ -1005,7 +1040,7 @@ void attack(display& gui, const gamemap& map,
|
|||
static const std::string hides("hides");
|
||||
a->second.set_state(hides,"");
|
||||
|
||||
battle_context bc(map, teams, units, state, info, attacker, defender, a->second.attacks()[attack_with]);
|
||||
battle_context bc(map, teams, units, state, info, attacker, defender, attack_with);
|
||||
const battle_context::unit_stats& a_stats = bc.get_attacker_stats();
|
||||
const battle_context::unit_stats& d_stats = bc.get_defender_stats();
|
||||
|
||||
|
|
|
@ -111,6 +111,7 @@ public:
|
|||
struct unit_stats
|
||||
{
|
||||
const attack_type *weapon; // The weapon used by the unit to attack the opponent, or NULL if there is none.
|
||||
int attack_num; // Index into unit->attacks() or -1 for none.
|
||||
bool is_attacker; // True if the unit is the attacker.
|
||||
bool is_poisoned; // True if the unit is poisoned at the beginning of the battle.
|
||||
bool is_slowed; // True if the unit is slowed at the beginning of the battle.
|
||||
|
@ -137,7 +138,7 @@ public:
|
|||
std::string plague_type; // The plague type used by the attack, if any.
|
||||
|
||||
unit_stats(const unit &u, const gamemap::location& u_loc,
|
||||
const attack_type *u_weapon, bool attacking,
|
||||
int u_attack_num, bool attacking,
|
||||
const unit &opp, const gamemap::location& opp_loc,
|
||||
const attack_type *opp_weapon,
|
||||
const std::map<gamemap::location,unit>& units,
|
||||
|
@ -151,7 +152,7 @@ public:
|
|||
battle_context(const gamemap& map, const std::vector<team>& teams, const std::map<gamemap::location,unit>& units,
|
||||
const gamestatus& status, const game_data& gamedata,
|
||||
const gamemap::location& attacker_loc, const gamemap::location& defender_loc,
|
||||
const attack_type& attacker_weapon);
|
||||
unsigned int attacker_weapon);
|
||||
|
||||
battle_context(const battle_context &other);
|
||||
~battle_context() { delete attacker_stats_; delete defender_stats_; }
|
||||
|
@ -178,6 +179,12 @@ private:
|
|||
unit_stats *attacker_stats_, *defender_stats_;
|
||||
};
|
||||
|
||||
int best_attack_weapon(const gamemap& map, const std::vector<team>& teams,
|
||||
const std::map<gamemap::location,unit>& units,
|
||||
const gamestatus& status, const game_data& gamedata,
|
||||
const gamemap::location& attacker_loc,
|
||||
const gamemap::location& defender_loc,
|
||||
std::vector<battle_context> &bc_vector);
|
||||
|
||||
//attack: executes an attack.
|
||||
void attack(display& gui, const gamemap& map,
|
||||
|
|
|
@ -1163,60 +1163,41 @@ bool mouse_handler::attack_enemy(unit_map::iterator attacker, unit_map::iterator
|
|||
const gamemap::location attacker_loc = attacker->first;
|
||||
const gamemap::location defender_loc = defender->first;
|
||||
|
||||
const std::vector<attack_type>& attacks = attacker->second.attacks();
|
||||
std::vector<std::string> items;
|
||||
std::vector<int> weapons;
|
||||
|
||||
int best_weapon_index = -1;
|
||||
unsigned int best_weapon_rating;
|
||||
|
||||
// Not every weapon gets included in bc_vector (attack_weight must be non-zero)
|
||||
std::vector<battle_context> bc_vector;
|
||||
int best = best_attack_weapon(map_, teams_, units_, status_, gameinfo_, attacker->first, defender->first, bc_vector);
|
||||
|
||||
for(size_t a = 0; a != attacks.size(); ++a) {
|
||||
// skip weapons with attack_weight=0
|
||||
if (attacks[a].attack_weight() > 0){
|
||||
weapons.push_back(a);
|
||||
|
||||
battle_context bc(map_, teams_, units_, status_, gameinfo_, attacker_loc, defender_loc, attacks[a]);
|
||||
bc_vector.push_back(bc);
|
||||
unsigned int weapon_rating = bc.rate_attacker_weapon(attacks[a].attack_weight());
|
||||
|
||||
if (best_weapon_index < 0 || best_weapon_rating < weapon_rating) {
|
||||
best_weapon_index = items.size();
|
||||
best_weapon_rating = weapon_rating;
|
||||
}
|
||||
for (unsigned int i = 0; i < bc_vector.size(); i++) {
|
||||
const battle_context::unit_stats& att = battle_context::unit_stats(bc_vector[i].get_attacker_stats());
|
||||
const battle_context::unit_stats& def = battle_context::unit_stats(bc_vector[i].get_defender_stats());
|
||||
config tmp_config;
|
||||
attack_type no_weapon(tmp_config, "fake_attack", "");
|
||||
const attack_type& attw = attack_type(*att.weapon);
|
||||
const attack_type& defw = attack_type(def.weapon ? *def.weapon : no_weapon);
|
||||
|
||||
const battle_context::unit_stats& att = battle_context::unit_stats(bc.get_attacker_stats());
|
||||
const battle_context::unit_stats& def = battle_context::unit_stats(bc.get_defender_stats());
|
||||
//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 = "";
|
||||
if (!attw.weapon_specials().empty() || !defw.weapon_specials().empty())
|
||||
special_pad = " ";
|
||||
|
||||
config tmp_config;
|
||||
attack_type no_weapon(tmp_config, "fake_attack", "");
|
||||
const attack_type& attw = attack_type(*att.weapon);
|
||||
const attack_type& defw = attack_type(def.weapon ? *def.weapon : no_weapon);
|
||||
|
||||
//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 = "";
|
||||
if (!attw.weapon_specials().empty() || !defw.weapon_specials().empty())
|
||||
special_pad = " ";
|
||||
|
||||
std::stringstream atts;
|
||||
atts << IMAGE_PREFIX << attw.icon() << COLUMN_SEPARATOR
|
||||
<< font::BOLD_TEXT << attw.name() << "\n" << att.damage << "-"
|
||||
<< att.num_blows << " " << attw.range() << " (" << att.chance_to_hit << "%)\n"
|
||||
<< attw.weapon_specials() << special_pad
|
||||
<< COLUMN_SEPARATOR << _("vs") << COLUMN_SEPARATOR
|
||||
<< font::BOLD_TEXT << defw.name() << "\n" << def.damage << "-"
|
||||
<< def.num_blows << " " << defw.range() << " (" << def.chance_to_hit << "%)\n"
|
||||
<< defw.weapon_specials() << special_pad << COLUMN_SEPARATOR
|
||||
<< IMAGE_PREFIX << defw.icon();
|
||||
|
||||
items.push_back(atts.str());
|
||||
std::stringstream atts;
|
||||
if ((int)i == best) {
|
||||
atts << DEFAULT_ITEM;
|
||||
}
|
||||
}
|
||||
atts << IMAGE_PREFIX << attw.icon() << COLUMN_SEPARATOR
|
||||
<< font::BOLD_TEXT << attw.name() << "\n" << att.damage << "-"
|
||||
<< att.num_blows << " " << attw.range() << " (" << att.chance_to_hit << "%)\n"
|
||||
<< attw.weapon_specials() << special_pad
|
||||
<< COLUMN_SEPARATOR << _("vs") << COLUMN_SEPARATOR
|
||||
<< font::BOLD_TEXT << defw.name() << "\n" << def.damage << "-"
|
||||
<< def.num_blows << " " << defw.range() << " (" << def.chance_to_hit << "%)\n"
|
||||
<< defw.weapon_specials() << special_pad << COLUMN_SEPARATOR
|
||||
<< IMAGE_PREFIX << defw.icon();
|
||||
|
||||
if (best_weapon_index >= 0) {
|
||||
items[best_weapon_index] = DEFAULT_ITEM + items[best_weapon_index];
|
||||
items.push_back(atts.str());
|
||||
}
|
||||
|
||||
//make it so that when we attack an enemy, the attacking unit
|
||||
|
@ -1245,9 +1226,9 @@ bool mouse_handler::attack_enemy(unit_map::iterator attacker, unit_map::iterator
|
|||
NULL,&buttons);
|
||||
}
|
||||
|
||||
cursor::set(cursor::NORMAL)
|
||||
;
|
||||
if(size_t(res) < weapons.size()) {
|
||||
cursor::set(cursor::NORMAL);
|
||||
if(size_t(res) < bc_vector.size()) {
|
||||
const battle_context::unit_stats &att = bc_vector[res].get_attacker_stats();
|
||||
|
||||
attacker->second.set_goto(gamemap::location());
|
||||
clear_undo_stack();
|
||||
|
@ -1261,13 +1242,13 @@ bool mouse_handler::attack_enemy(unit_map::iterator attacker, unit_map::iterator
|
|||
|
||||
const bool defender_human = teams_[defender->second.side()-1].is_human();
|
||||
|
||||
recorder.add_attack(attacker_loc,defender_loc,weapons[res]);
|
||||
recorder.add_attack(attacker_loc,defender_loc,att.attack_num);
|
||||
|
||||
//MP_COUNTDOWN grant time bonus for attacking
|
||||
current_team().set_action_bonus_count(1 + current_team().action_bonus_count());
|
||||
|
||||
try {
|
||||
attack(*gui_,map_,teams_,attacker_loc,defender_loc,weapons[res],units_,status_,gameinfo_);
|
||||
attack(*gui_,map_,teams_,attacker_loc,defender_loc,att.attack_num,units_,status_,gameinfo_);
|
||||
} catch(end_level_exception&) {
|
||||
//if the level ends due to a unit being killed, still see if
|
||||
//either the attacker or defender should advance
|
||||
|
|
Loading…
Add table
Reference in a new issue