mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-11 16:53:10 +00:00
Tie attack_prediction into using battle_context's unit stats,
...so you can use them together.
This commit is contained in:
parent
ba618b0680
commit
3367b8d31b
@ -895,6 +895,8 @@ battle_context::unit_stats::unit_stats(const unit &u, const gamemap::location& u
|
||||
// drain = normal damage / 2 and slow_drain = slow_damage / 2.
|
||||
damage = round_damage(base_damage, damage_multiplier, 10000);
|
||||
slow_damage = round_damage(base_damage, damage_multiplier, 20000);
|
||||
if (is_slowed)
|
||||
damage = slow_damage;
|
||||
|
||||
// Compute the number of blows and handle swarm.
|
||||
unit_ability_list swarm_specials = weapon->get_specials("attacks");
|
||||
|
@ -122,14 +122,14 @@ public:
|
||||
bool berserk; // Berserk special is used, either by this unit or his opponent.
|
||||
bool firststrike; // Attack has firststrike special.
|
||||
|
||||
int hp; // Hitpoints of the unit at the beginning of the battle.
|
||||
int max_hp; // Maximum hitpoints of the unit.
|
||||
int chance_to_hit; // Effective chance to hit (all factors accounted for).
|
||||
int damage; // Effective damage of the weapon (all factors accounted for, except slow state).
|
||||
int slow_damage; // Effective damage if unit is/becomes slowed.
|
||||
int num_blows; // Effective number of blows, takes swarm into account.
|
||||
int swarm_min; // Minimum number of blows with swarm (equal to num_blows if swarm isn't used).
|
||||
int swarm_max; // Maximum number of blows with swarm (equal to num_blows if swarm isn't used).
|
||||
unsigned int hp; // Hitpoints of the unit at the beginning of the battle.
|
||||
unsigned int max_hp; // Maximum hitpoints of the unit.
|
||||
unsigned int chance_to_hit; // Effective chance to hit as a percentage (all factors accounted for).
|
||||
int damage; // Effective damage of the weapon (all factors accounted for).
|
||||
int slow_damage; // Effective damage if unit becomes slowed (== damage, if already slowed)
|
||||
unsigned int num_blows; // Effective number of blows, takes swarm into account.
|
||||
unsigned int swarm_min; // Minimum number of blows with swarm (equal to num_blows if swarm isn't used).
|
||||
unsigned int swarm_max; // Maximum number of blows with swarm (equal to num_blows if swarm isn't used).
|
||||
|
||||
unit_stats(const unit &u, const gamemap::location& u_loc,
|
||||
const attack_type *u_weapon, bool attacking,
|
||||
|
@ -425,78 +425,43 @@ void prob_matrix::receive_blow_a(unsigned damage, double hit_chance,
|
||||
|
||||
};
|
||||
|
||||
combatant::combatant(unsigned hp, unsigned max_hp, bool slowed,
|
||||
bool could_ever_drain)
|
||||
: hp_dist(could_ever_drain ? max_hp+1: hp+1), hp_(hp), max_hp_(max_hp),
|
||||
slowed_(slowed)
|
||||
combatant::combatant(const battle_context::unit_stats &u)
|
||||
: hp_dist(u.drains ? u.max_hp+1: u.hp+1),
|
||||
untouched(1.0),
|
||||
u_(u),
|
||||
hit_chances_(u.num_blows, u.chance_to_hit / 100.0)
|
||||
{
|
||||
}
|
||||
|
||||
// Select a weapon.
|
||||
void combatant::set_weapon(unsigned num_attacks, bool drains, bool berserk,
|
||||
bool swarm, bool firststrike)
|
||||
// For swarm, whether we get an attack depends on HP distribution from
|
||||
// previous combat. So we roll this into our P(hitting), since no
|
||||
// attack is equivalent to missing.
|
||||
void combatant::adjust_hitchance()
|
||||
{
|
||||
base_num_attacks_ = num_attacks;
|
||||
drains_ = drains;
|
||||
berserk_ = berserk;
|
||||
swarm_ = swarm;
|
||||
firststrike_ = firststrike;
|
||||
}
|
||||
if (summary[0].empty() || u_.swarm_min == u_.swarm_max)
|
||||
return;
|
||||
|
||||
unsigned combatant::num_attacks(unsigned int hp) const
|
||||
{
|
||||
if (swarm_)
|
||||
return base_num_attacks_ - (base_num_attacks_*(max_hp_-hp)/max_hp_);
|
||||
hit_chances_ = std::vector<double>(u_.swarm_max);
|
||||
double alive_prob;
|
||||
|
||||
if (summary[1].empty())
|
||||
alive_prob = 1 - summary[0][0];
|
||||
else
|
||||
return base_num_attacks_;
|
||||
}
|
||||
alive_prob = 1 - summary[0][0] - summary[1][0];
|
||||
|
||||
// Set effect against this particular opponent.
|
||||
void combatant::set_effectiveness(unsigned damage, double hit_chance,
|
||||
bool slows)
|
||||
{
|
||||
slows_ = slows;
|
||||
base_hit_chance_ = hit_chance;
|
||||
if (slowed_)
|
||||
damage_ = damage / 2;
|
||||
else
|
||||
damage_ = damage;
|
||||
|
||||
if (!swarm_ || summary[0].empty())
|
||||
hit_chances_ = std::vector<double>(num_attacks(hp_), hit_chance);
|
||||
else {
|
||||
// Whether we get an attack depends on HP distribution from previous
|
||||
// combat. So we roll this into our P(hitting), since no attack is
|
||||
// equivalent to missing.
|
||||
hit_chances_ = std::vector<double>(base_num_attacks_);
|
||||
double alive_prob;
|
||||
|
||||
if (summary[1].empty())
|
||||
alive_prob = 1 - summary[0][0];
|
||||
else
|
||||
alive_prob = 1 - summary[0][0] - summary[1][0];
|
||||
|
||||
for (unsigned int i = 1; i <= max_hp_; i++) {
|
||||
double prob = summary[0][i];
|
||||
if (!summary[1].empty())
|
||||
prob += summary[1][i];
|
||||
for (unsigned int j = 0; j < num_attacks(i); j++)
|
||||
hit_chances_[j] += prob * hit_chance / alive_prob;
|
||||
}
|
||||
unsigned int i;
|
||||
for (i = 1; i <= u_.max_hp; i++) {
|
||||
double prob = summary[0][i];
|
||||
if (!summary[1].empty())
|
||||
prob += summary[1][i];
|
||||
for (unsigned int j = 0; j < u_.swarm_min + (u_.swarm_max - (double)u_.swarm_min) * u_.hp / u_.max_hp; j++)
|
||||
hit_chances_[j] += prob * u_.chance_to_hit / 100.0 / alive_prob;
|
||||
}
|
||||
debug(("\nhit_chances_ (base %u%%):", (unsigned)(hit_chance * 100.0 + 0.5)));
|
||||
for (unsigned int i = 0; i < base_num_attacks_; i++)
|
||||
debug((" %.2f", hit_chances_[i]));
|
||||
debug(("\n"));
|
||||
}
|
||||
|
||||
void combatant::reset()
|
||||
{
|
||||
for (unsigned int i = 0; i < hp_dist.size(); i++)
|
||||
hp_dist[i] = 0.0;
|
||||
summary[0] = std::vector<double>();
|
||||
summary[1] = std::vector<double>();
|
||||
hit_chances_ = std::vector<double>(num_attacks(hp_), base_hit_chance_);
|
||||
debug(("\nhit_chances_ (base %u%%):", u_.chance_to_hit));
|
||||
for (i = 0; i < u_.swarm_max; i++)
|
||||
debug((" %.2f", hit_chances_[i] * 100.0 + 0.5));
|
||||
debug(("\n"));
|
||||
}
|
||||
|
||||
// Two man enter. One man leave!
|
||||
@ -505,59 +470,45 @@ void combatant::reset()
|
||||
// Um, ok, it was a stupid thing to say.
|
||||
void combatant::fight(combatant &opp)
|
||||
{
|
||||
unsigned int i, rounds = berserk_ || opp.berserk_ ? 30 : 1;
|
||||
// FIXME: Don't hardcode 30 here.
|
||||
unsigned int i, rounds = u_.berserk || opp.u_.berserk ? 30 : 1;
|
||||
|
||||
// If defender has firststrike and we don't, reverse.
|
||||
if (opp.firststrike_ && !firststrike_) {
|
||||
if (opp.u_.firststrike && !u_.firststrike) {
|
||||
opp.fight(*this);
|
||||
return;
|
||||
}
|
||||
|
||||
debug(("A: %u %u %u %2g%% ",
|
||||
damage_, base_num_attacks_, hp_, base_hit_chance_*100.0));
|
||||
if (drains_)
|
||||
debug(("drains,"));
|
||||
if (slows_ && !opp.slowed_)
|
||||
debug(("slows,"));
|
||||
if (berserk_)
|
||||
debug(("berserk,"));
|
||||
if (swarm_)
|
||||
debug(("swarm,"));
|
||||
debug(("maxhp=%u\n", hp_dist.size()-1));
|
||||
debug(("B: %u %u %u %2g%% ", opp.damage_, opp.base_num_attacks_, opp.hp_,
|
||||
opp.base_hit_chance_*100.0));
|
||||
if (opp.drains_)
|
||||
debug(("drains,"));
|
||||
if (opp.slows_ && !slowed_)
|
||||
debug(("slows,"));
|
||||
if (opp.berserk_)
|
||||
debug(("berserk,"));
|
||||
if (opp.swarm_)
|
||||
debug(("swarm,"));
|
||||
debug(("maxhp=%u\n", opp.hp_dist.size()-1));
|
||||
#ifdef DEBUG
|
||||
printf("A:\n");
|
||||
u_.dump();
|
||||
printf("B:\n");
|
||||
opp.u_.dump();
|
||||
#endif
|
||||
|
||||
// If we've fought before and we have swarm, we must adjust cth array.
|
||||
adjust_hitchance();
|
||||
opp.adjust_hitchance();
|
||||
|
||||
prob_matrix m(hp_dist.size()-1, opp.hp_dist.size()-1,
|
||||
slows_ && !opp.slowed_, opp.slows_ && !slowed_, hp_, opp.hp_,
|
||||
summary, opp.summary);
|
||||
u_.slows && !opp.u_.is_slowed, opp.u_.slows && !u_.is_slowed,
|
||||
u_.hp, opp.u_.hp, summary, opp.summary);
|
||||
|
||||
unsigned max_attacks = maximum(hit_chances_.size(),
|
||||
opp.hit_chances_.size());
|
||||
unsigned max_attacks = maximum(hit_chances_.size(), opp.hit_chances_.size());
|
||||
|
||||
debug(("A gets %u attacks, B %u\n",
|
||||
hit_chances_.size(),
|
||||
opp.hit_chances_.size()));
|
||||
debug(("A gets %u attacks, B %u\n", hit_chances_.size(), opp.hit_chances_.size()));
|
||||
do {
|
||||
for (i = 0; i < max_attacks; i++) {
|
||||
if (i < hit_chances_.size()) {
|
||||
debug(("A strikes\n"));
|
||||
m.receive_blow_b(damage_, hit_chances_[i],
|
||||
slows_ && !opp.slowed_, drains_);
|
||||
m.receive_blow_b(u_.damage, hit_chances_[i],
|
||||
u_.slows && !opp.u_.is_slowed, u_.drains);
|
||||
m.dump();
|
||||
}
|
||||
if (i < opp.hit_chances_.size()) {
|
||||
debug(("B strikes\n"));
|
||||
m.receive_blow_a(opp.damage_, opp.hit_chances_[i],
|
||||
opp.slows_ && !slowed_, opp.drains_);
|
||||
m.receive_blow_a(opp.u_.damage, opp.hit_chances_[i],
|
||||
opp.u_.slows && !u_.is_slowed, opp.u_.drains);
|
||||
m.dump();
|
||||
}
|
||||
}
|
||||
@ -583,8 +534,8 @@ void combatant::fight(combatant &opp)
|
||||
}
|
||||
|
||||
// FIXME: This is approximate: we could drain, then get hit.
|
||||
untouched = hp_dist[hp_];
|
||||
opp.untouched = opp.hp_dist[opp.hp_];
|
||||
untouched = hp_dist[u_.hp];
|
||||
opp.untouched = opp.hp_dist[opp.u_.hp];
|
||||
}
|
||||
|
||||
#if defined(BENCHMARK) || defined(CHECK)
|
||||
|
@ -4,54 +4,31 @@
|
||||
#define ATTACK_PREDICTION_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include "actions.hpp"
|
||||
|
||||
// This encapsulates all we need to know for this combat.
|
||||
struct combatant
|
||||
{
|
||||
// Construct a combatant.
|
||||
// As an optimization, if it has no weapons which could drain,
|
||||
// we can simply never calculate hitpoints > current hp.
|
||||
combatant(unsigned hp, unsigned max_hp, bool slowed,
|
||||
bool could_ever_drain = true);
|
||||
combatant(const battle_context::unit_stats &u);
|
||||
|
||||
// Select a weapon.
|
||||
void set_weapon(unsigned num_attacks, bool drains, bool berserk,
|
||||
bool swarm, bool firststrike);
|
||||
|
||||
// Set effect against this particular opponent.
|
||||
void set_effectiveness(unsigned damage, double hit_chance, bool slows);
|
||||
|
||||
// Fight!
|
||||
// Simulate a fight! Can be called multiple times for cumulative calculations.
|
||||
void fight(combatant &opponent);
|
||||
|
||||
// Only used in benchmarking.
|
||||
void reset();
|
||||
void print(const char label[], unsigned int battle) const;
|
||||
|
||||
// Resulting probability distribution (may NOT be as large as max_hp)
|
||||
std::vector<double> hp_dist;
|
||||
|
||||
// Resulting chance we were not hit by this opponent (important if
|
||||
// it poisons)
|
||||
// Resulting chance we were not hit by this opponent (important if it poisons)
|
||||
double untouched;
|
||||
|
||||
private:
|
||||
// How many attacks? (Matters for swarm).
|
||||
unsigned num_attacks(unsigned int hp) const;
|
||||
// We must adjust for swarm after every combat.
|
||||
void combatant::adjust_hitchance();
|
||||
|
||||
const battle_context::unit_stats &u_;
|
||||
|
||||
// Usually uniform, but if we have swarm, then can be different.
|
||||
std::vector<double> hit_chances_;
|
||||
double base_hit_chance_;
|
||||
|
||||
// Starting hitpoints, max hp.
|
||||
unsigned hp_, max_hp_;
|
||||
|
||||
// Are we slowed already? (Halves damage, can't be slowed again).
|
||||
bool slowed_;
|
||||
|
||||
// Weapon stats.
|
||||
unsigned base_num_attacks_, damage_;
|
||||
bool drains_, slows_, berserk_, swarm_, firststrike_;
|
||||
|
||||
// Summary of matrix used to calculate last battle (unslowed & slowed).
|
||||
std::vector<double> summary[2];
|
||||
|
Loading…
x
Reference in New Issue
Block a user