battle_context::battle_context(const unit_map& units, const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon, int defender_weapon, double aggression, const combatant *prev_def, const unit* attacker_ptr) : attacker_stats_(nullptr), defender_stats_(nullptr), attacker_combatant_(nullptr), defender_combatant_(nullptr) { const unit &attacker = attacker_ptr ? *attacker_ptr : *units.find(attacker_loc); const unit &defender = *units.find(defender_loc); const double harm_weight = 1.0 - aggression; if (attacker_weapon == -1 && attacker.attacks().size() == 1 && attacker.attacks()[0].attack_weight() > 0 ) attacker_weapon = 0; if (attacker_weapon == -1) { attacker_weapon = choose_attacker_weapon(attacker, defender, units, attacker_loc, defender_loc, harm_weight, &defender_weapon, prev_def); } else if (defender_weapon == -1) { defender_weapon = choose_defender_weapon(attacker, defender, attacker_weapon, units, attacker_loc, defender_loc, prev_def); } // If those didn't have to generate statistics, do so now. if (!attacker_stats_) { const attack_type *adef = nullptr; const attack_type *ddef = nullptr; if (attacker_weapon >= 0) { VALIDATE(attacker_weapon < static_cast<int>(attacker.attacks().size()), _("An invalid attacker weapon got selected.")); adef = &attacker.attacks()[attacker_weapon]; } if (defender_weapon >= 0) { VALIDATE(defender_weapon < static_cast<int>(defender.attacks().size()), _("An invalid defender weapon got selected.")); ddef = &defender.attacks()[defender_weapon]; } assert(!defender_stats_ && !attacker_combatant_ && !defender_combatant_); attacker_stats_ = new battle_context_unit_stats(attacker, attacker_loc, attacker_weapon, true, defender, defender_loc, ddef, units); defender_stats_ = new battle_context_unit_stats(defender, defender_loc, defender_weapon, false, attacker, attacker_loc, adef, units); } // There have been various bugs where only one of these was set assert(attacker_stats_); assert(defender_stats_); }
int battle_context::choose_attacker_weapon(const unit &attacker, const unit &defender, const unit_map& units, const map_location& attacker_loc, const map_location& defender_loc, double harm_weight, int *defender_weapon, const combatant *prev_def) { std::vector<unsigned int> choices; // What options does attacker have? unsigned int i; for (i = 0; i < attacker.attacks().size(); ++i) { const attack_type &att = attacker.attacks()[i]; if (att.attack_weight() > 0) { choices.push_back(i); } } if (choices.empty()) return -1; if (choices.size() == 1) { *defender_weapon = choose_defender_weapon(attacker, defender, choices[0], units, attacker_loc, defender_loc, prev_def); return choices[0]; } // Multiple options: simulate them, save best. battle_context_unit_stats *best_att_stats = NULL, *best_def_stats = NULL; combatant *best_att_comb = NULL, *best_def_comb = NULL; for (i = 0; i < choices.size(); ++i) { const attack_type &att = attacker.attacks()[choices[i]]; int def_weapon = choose_defender_weapon(attacker, defender, choices[i], units, attacker_loc, defender_loc, prev_def); // If that didn't simulate, do so now. if (!attacker_combatant_) { const attack_type *def = NULL; if (def_weapon >= 0) { def = &defender.attacks()[def_weapon]; } attacker_stats_ = new battle_context_unit_stats(attacker, attacker_loc, choices[i], true, defender, defender_loc, def, units); defender_stats_ = new battle_context_unit_stats(defender, defender_loc, def_weapon, false, attacker, attacker_loc, &att, units); attacker_combatant_ = new combatant(*attacker_stats_); defender_combatant_ = new combatant(*defender_stats_, prev_def); attacker_combatant_->fight(*defender_combatant_); } if (!best_att_comb || better_combat(*attacker_combatant_, *defender_combatant_, *best_att_comb, *best_def_comb, harm_weight)) { delete best_att_comb; delete best_def_comb; delete best_att_stats; delete best_def_stats; best_att_comb = attacker_combatant_; best_def_comb = defender_combatant_; best_att_stats = attacker_stats_; best_def_stats = defender_stats_; } else { delete attacker_combatant_; delete defender_combatant_; delete attacker_stats_; delete defender_stats_; } attacker_combatant_ = NULL; defender_combatant_ = NULL; attacker_stats_ = NULL; defender_stats_ = NULL; } attacker_combatant_ = best_att_comb; defender_combatant_ = best_def_comb; attacker_stats_ = best_att_stats; defender_stats_ = best_def_stats; *defender_weapon = defender_stats_->attack_num; return attacker_stats_->attack_num; }
int battle_context::choose_attacker_weapon(const unit& attacker, const unit& defender, const unit_map& units, const map_location& attacker_loc, const map_location& defender_loc, double harm_weight, int* defender_weapon, const combatant* prev_def) { std::vector<unsigned int> choices; // What options does attacker have? unsigned int i; for(i = 0; i < attacker.attacks().size(); ++i) { const attack_type& att = attacker.attacks()[i]; if(att.attack_weight() > 0) { choices.push_back(i); } } if(choices.empty()) { return -1; } if(choices.size() == 1) { *defender_weapon = choose_defender_weapon(attacker, defender, choices[0], units, attacker_loc, defender_loc, prev_def); const_attack_ptr def_weapon = *defender_weapon >= 0 ? defender.attacks()[*defender_weapon].shared_from_this() : nullptr; attacker_stats_.reset(new battle_context_unit_stats( attacker, attacker_loc, choices[0], true, defender, defender_loc, def_weapon, units)); if(attacker_stats_->disable) { return -1; } const attack_type& att = attacker.attacks()[choices[0]]; defender_stats_.reset(new battle_context_unit_stats( defender, defender_loc, *defender_weapon, false, attacker, attacker_loc, att.shared_from_this(), units)); return choices[0]; } // Multiple options: simulate them, save best. std::unique_ptr<battle_context_unit_stats> best_att_stats(nullptr); std::unique_ptr<battle_context_unit_stats> best_def_stats(nullptr); std::unique_ptr<combatant> best_att_comb(nullptr); std::unique_ptr<combatant> best_def_comb(nullptr); for(i = 0; i < choices.size(); ++i) { const attack_type& att = attacker.attacks()[choices[i]]; int def_weapon = choose_defender_weapon(attacker, defender, choices[i], units, attacker_loc, defender_loc, prev_def); // If that didn't simulate, do so now. if(!attacker_combatant_) { const_attack_ptr def = nullptr; if(def_weapon >= 0) { def = defender.attacks()[def_weapon].shared_from_this(); } attacker_stats_.reset(new battle_context_unit_stats( attacker, attacker_loc, choices[i], true, defender, defender_loc, def, units)); if(attacker_stats_->disable) { continue; } defender_stats_.reset(new battle_context_unit_stats( defender, defender_loc, def_weapon, false, attacker, attacker_loc, att.shared_from_this(), units)); attacker_combatant_.reset(new combatant(*attacker_stats_)); defender_combatant_.reset(new combatant(*defender_stats_, prev_def)); attacker_combatant_->fight(*defender_combatant_); } else { if(attacker_stats_ != nullptr && attacker_stats_->disable) { continue; } } if(!best_att_comb || better_combat(*attacker_combatant_, *defender_combatant_, *best_att_comb, *best_def_comb, harm_weight) ) { best_att_comb = std::move(attacker_combatant_); best_def_comb = std::move(defender_combatant_); best_att_stats = std::move(attacker_stats_); best_def_stats = std::move(defender_stats_); } attacker_combatant_.reset(); defender_combatant_.reset(); attacker_stats_.reset(); defender_stats_.reset(); } attacker_combatant_ = std::move(best_att_comb); defender_combatant_ = std::move(best_def_comb); attacker_stats_ = std::move(best_att_stats); defender_stats_ = std::move(best_def_stats); // These currently mean the same thing, but assumptions like that have been broken before if(!defender_stats_ || !attacker_stats_) { return -1; } *defender_weapon = defender_stats_->attack_num; return attacker_stats_->attack_num; }