Use intrusive pointers for unit attacks

This is primarily for the purpose of the Lua API, to enable attack data to persist
even if a unit no longer owns the attack.
This commit is contained in:
Celtic Minstrel 2016-08-27 18:51:36 -04:00
parent 496a523fa2
commit b143ea4397
17 changed files with 96 additions and 110 deletions

View file

@ -570,7 +570,8 @@ int battle_context::choose_attacker_weapon(const unit &attacker,
return attacker_stats_->attack_num;
}
/** @todo FIXME: Hand previous defender unit in here. */
/** @todo FIXME: Hand previous defender unit in here.
*/
int battle_context::choose_defender_weapon(const unit &attacker,
const unit &defender, unsigned attacker_weapon, const unit_map& units,
const map_location& attacker_loc, const map_location& defender_loc,
@ -665,7 +666,7 @@ int battle_context::choose_defender_weapon(const unit &attacker,
namespace {
void refresh_weapon_index(int& weap_index, std::string const& weap_id, std::vector<attack_type> const& attacks)
void refresh_weapon_index(int& weap_index, std::string const& weap_id, attack_itors attacks)
{
if(attacks.empty()) {
//no attacks to choose from

View file

@ -168,15 +168,14 @@ void aspect_attacks_base::do_attack_analysis(
//
// See if the unit has the slow ability -- units with slow only attack first.
bool backstab = false, slow = false;
std::vector<attack_type>& attacks = unit_itor->attacks();
for(std::vector<attack_type>::iterator a = attacks.begin(); a != attacks.end(); ++a) {
for(const attack_type& a : unit_itor->attacks()) {
// For speed, just assume these specials will be active if
// they are present.
if ( a->get_special_bool("backstab", true) ) {
if ( a.get_special_bool("backstab", true) ) {
backstab = true;
}
if ( a->get_special_bool("slow", true) ) {
if ( a.get_special_bool("slow", true) ) {
slow = true;
}
}

View file

@ -837,9 +837,8 @@ double move_to_targets_phase::rate_group(const std::set<map_location>& group, co
defense /= battlefield.size();
int best_attack = 0;
const std::vector<attack_type>& attacks = un.attacks();
for(std::vector<attack_type>::const_iterator a = attacks.begin(); a != attacks.end(); ++a) {
const int strength = a->num_attacks()*a->damage();
for(const attack_type& a : un.attacks()) {
const int strength = a.num_attacks() * a.damage();
best_attack = std::max<int>(strength,best_attack);
}

View file

@ -1118,8 +1118,8 @@ void recruitment::simulate_attack(
ERR_AI_RECRUITMENT << "nullptr pointer in simulate_attack()" << std::endl;
return;
}
const std::vector<attack_type> attacker_weapons = attacker->attacks();
const std::vector<attack_type> defender_weapons = defender->attacks();
const_attack_itors attacker_weapons = attacker->attacks();
const_attack_itors defender_weapons = defender->attacks();
std::shared_ptr<attack_simulation> best_att_attack;

View file

@ -70,7 +70,7 @@ class unit_adapter {
}
}
const std::vector<attack_type>& attacks() const {
const_attack_itors attacks() const {
if(unit_type_ != nullptr) {
return unit_type_->attacks();
} else {
@ -1540,42 +1540,15 @@ private:
if(u1.is_null() || u2.is_null()) {
return variant();
}
std::vector<attack_type> attacks_tmp;
std::vector<attack_type>& attacks = attacks_tmp;
//we have to make sure that this function works with any combination of unit_callable/unit_type_callable passed to it
const unit_callable* u_attacker = try_convert_variant<unit_callable>(u1);
if (u_attacker)
{
attacks = u_attacker->get_unit().attacks();
} else
{
const unit_type_callable* u_t_attacker = convert_variant<unit_type_callable>(u1);
attacks_tmp = u_t_attacker->get_unit_type().attacks();
}
const unit_callable* u_defender = try_convert_variant<unit_callable>(u2);
if (u_defender)
{
const unit& defender = u_defender->get_unit();
int best = 0;
for(std::vector<attack_type>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
const int dmg = round_damage(i->damage(), defender.damage_from(*i, false, map_location()), 100) * i->num_attacks();
if(dmg > best)
best = dmg;
}
return variant(best);
} else
{
const unit_type& defender = convert_variant<unit_type_callable>(u2)->get_unit_type();
int best = 0;
for(std::vector<attack_type>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
const int dmg = round_damage(i->damage(), defender.movement_type().resistance_against(*i), 100) * i->num_attacks();
if(dmg > best)
best = dmg;
}
return variant(best);
unit_adapter u_attacker(u1), u_defender(u2);
int best = 0;
for(const attack_type& atk : u_attacker.attacks()) {
const int dmg = round_damage(atk.damage(), u_defender.damage_from(atk), 100) * atk.num_attacks();
if(dmg > best)
best = dmg;
}
return variant(best);
}
};
@ -1591,9 +1564,7 @@ private:
int highest_melee_damage = 0;
int highest_ranged_damage = 0;
std::vector<attack_type> attacks = attacker.attacks();
for (const attack_type &attack : attacks) {
for (const attack_type &attack : attacker.attacks()) {
const int dmg = round_damage(attack.damage(), defender.damage_from(attack), 100) * attack.num_attacks();
if (attack.range() == "melee") {
highest_melee_damage = std::max(highest_melee_damage, dmg);

View file

@ -101,7 +101,8 @@ static inline ttree_view_node& add_name_tree_node(ttree_view_node& header_node,
* Both unit and unit_type use the same format (vector of attack_types) for their
* attack data, meaning we can keep this as a helper function.
*/
void tunit_preview_pane::print_attack_details(const std::vector<attack_type>& attacks, ttree_view_node& parent_node)
template<typename T>
void tunit_preview_pane::print_attack_details(T attacks, ttree_view_node& parent_node)
{
if (attacks.empty()) {
return;

View file

@ -18,7 +18,6 @@
#include <string>
class attack_type;
class unit;
class unit_type;
@ -106,7 +105,8 @@ private:
std::string image_mods_;
void print_attack_details(const std::vector<attack_type>& attacks, ttree_view_node& parent_node);
template<typename T> // This is only a template to avoid including units/attack.hpp
void print_attack_details(T attacks, ttree_view_node& parent_node);
enum tstate {
ENABLED

View file

@ -402,11 +402,9 @@ std::vector<topic> generate_weapon_special_topics(const bool sort_generated)
if (description_type(type) != FULL_DESCRIPTION)
continue;
std::vector<attack_type> attacks = type.attacks();
for (std::vector<attack_type>::const_iterator it = attacks.begin();
it != attacks.end(); ++it) {
for (const attack_type& atk : type.attacks()) {
std::vector<std::pair<t_string, t_string> > specials = it->special_tooltips();
std::vector<std::pair<t_string, t_string> > specials = atk.special_tooltips();
for ( size_t i = 0; i != specials.size(); ++i )
{
special_description.insert(std::make_pair(specials[i].first, specials[i].second));

View file

@ -491,8 +491,7 @@ std::string unit_topic_generator::operator()() const {
ss << "\n\n" << detailed_description;
// Print the different attacks a unit has, if it has any.
std::vector<attack_type> attacks = type_.attacks();
if (!attacks.empty()) {
if (!type_.attacks().empty()) {
// Print headers for the table.
ss << "\n\n<header>text='" << escape(_("unit help^Attacks"))
<< "'</header>\n\n";
@ -508,31 +507,29 @@ std::string unit_topic_generator::operator()() const {
push_header(first_row, _("Special"));
table.push_back(first_row);
// Print information about every attack.
for(std::vector<attack_type>::const_iterator attack_it = attacks.begin(),
attack_end = attacks.end();
attack_it != attack_end; ++attack_it) {
std::string lang_weapon = attack_it->name();
std::string lang_type = string_table["type_" + attack_it->type()];
for(const attack_type& attack : type_.attacks()) {
std::string lang_weapon = attack.name();
std::string lang_type = string_table["type_" + attack.type()];
std::vector<item> row;
std::stringstream attack_ss;
attack_ss << "<img>src='" << (*attack_it).icon() << "'</img>";
row.push_back(std::make_pair(attack_ss.str(),image_width(attack_it->icon())));
attack_ss << "<img>src='" << attack.icon() << "'</img>";
row.push_back(std::make_pair(attack_ss.str(),image_width(attack.icon())));
push_tab_pair(row, lang_weapon);
push_tab_pair(row, lang_type);
attack_ss.str(clear_stringstream);
attack_ss << attack_it->damage() << utils::unicode_en_dash << attack_it->num_attacks()
<< " " << attack_it->accuracy_parry_description();
attack_ss << attack.damage() << utils::unicode_en_dash << attack.num_attacks()
<< " " << attack.accuracy_parry_description();
push_tab_pair(row, attack_ss.str());
attack_ss.str(clear_stringstream);
if ((*attack_it).min_range() > 1 || (*attack_it).max_range() > 1) {
attack_ss << (*attack_it).min_range() << "-" << (*attack_it).max_range() << ' ';
if (attack.min_range() > 1 || attack.max_range() > 1) {
attack_ss << attack.min_range() << "-" << attack.max_range() << ' ';
}
attack_ss << string_table["range_" + (*attack_it).range()];
attack_ss << string_table["range_" + attack.range()];
push_tab_pair(row, attack_ss.str());
attack_ss.str(clear_stringstream);
// Show this attack's special, if it has any. Cross
// reference it to the section describing the special.
std::vector<std::pair<t_string, t_string> > specials = attack_it->special_tooltips();
std::vector<std::pair<t_string, t_string> > specials = attack.special_tooltips();
if (!specials.empty()) {
std::string lang_special = "";
const size_t specials_size = specials.size();

View file

@ -95,7 +95,7 @@ static int impl_unit_attacks_get(lua_State *L)
return luaL_argerror(L, 1, "unknown unit");
}
const attack_type* attack = nullptr;
const std::vector<attack_type>& attacks = u ? u->get()->attacks() : ut->attacks();
const_attack_itors attacks = u ? u->get()->attacks() : ut->attacks();
if(!lua_isnumber(L,2)) {
std::string attack_id = luaL_checkstring(L, 2);
for(const attack_type& at : attacks) {

View file

@ -21,6 +21,10 @@
#include <string>
#include <vector>
#include <boost/iterator/indirect_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
class unit_ability_list;
//the 'attack type' is the type of attack, how many times it strikes,
@ -121,5 +125,29 @@ private:
int movement_used_;
int parry_;
config specials_;
size_t ref_count = 0;
friend void intrusive_ptr_add_ref(attack_type* atk) {
++atk->ref_count;
}
friend void intrusive_ptr_release(attack_type* atk) {
--atk->ref_count;
}
};
using attack_ptr = boost::intrusive_ptr<attack_type>;
using const_attack_ptr = boost::intrusive_ptr<const attack_type>;
using attack_list = std::vector<attack_ptr>;
using attack_itors = boost::iterator_range<boost::indirect_iterator<attack_list::iterator>>;
using const_attack_itors = boost::iterator_range<boost::indirect_iterator<attack_list::const_iterator>>;
inline attack_itors make_attack_itors(attack_list& atks) {
return boost::make_iterator_range(boost::make_indirect_iterator(atks.begin()), boost::make_indirect_iterator(atks.end()));
}
inline const_attack_itors make_attack_itors(const attack_list& atks) {
return boost::make_iterator_range(boost::make_indirect_iterator(atks.begin()), boost::make_indirect_iterator(atks.end()));
}
#endif

View file

@ -414,10 +414,8 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l
if (vcfg.has_child("has_attack")) {
const vconfig& weap_filter = vcfg.child("has_attack");
bool has_weapon = false;
const std::vector<attack_type>& attacks = u.attacks();
for(std::vector<attack_type>::const_iterator i = attacks.begin();
i != attacks.end(); ++i) {
if(i->matches_filter(weap_filter.get_parsed_config())) {
for(const attack_type& a : u.attacks()) {
if(a.matches_filter(weap_filter.get_parsed_config())) {
has_weapon = true;
break;
}
@ -428,10 +426,8 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l
} else if (!vcfg["has_weapon"].blank()) {
std::string weapon = vcfg["has_weapon"];
bool has_weapon = false;
const std::vector<attack_type>& attacks = u.attacks();
for(std::vector<attack_type>::const_iterator i = attacks.begin();
i != attacks.end(); ++i) {
if(i->id() == weapon) {
for(const attack_type& a : u.attacks()) {
if(a.id() == weapon) {
has_weapon = true;
break;
}

View file

@ -484,17 +484,17 @@ const std::vector<unit_animation>& unit_type::animations() const {
return animations_;
}
const std::vector<attack_type>& unit_type::attacks() const
const_attack_itors unit_type::attacks() const
{
if(!attacks_cache_.empty()) {
return attacks_cache_;
return make_attack_itors(attacks_cache_);
}
for (const config &att : cfg_.child_range("attack")) {
attacks_cache_.push_back(attack_type(att));
attacks_cache_.emplace_back(new attack_type(att));
}
return attacks_cache_;
return make_attack_itors(attacks_cache_);
}

View file

@ -148,7 +148,7 @@ public:
const std::string& flag_rgb() const;
const std::vector<attack_type>& attacks() const;
const_attack_itors attacks() const;
const movetype & movement_type() const { return movement_type_; }
int experience_needed(bool with_acceleration=true) const;
@ -244,7 +244,7 @@ private:
const config &cfg_;
mutable config unit_cfg_; /// Generated as needed via get_cfg_for_units().
mutable bool built_unit_cfg_;
mutable std::vector<attack_type> attacks_cache_;
mutable attack_list attacks_cache_;
std::string id_;
std::string debug_id_; /// A suffix for id_, used when logging messages.

View file

@ -803,10 +803,10 @@ void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const m
attack_type *secondary = nullptr;
Uint32 text_color;
unit_animation::hit_type hits= unit_animation::hit_type::INVALID;
std::vector<attack_type> attacks = u->attacks();
std::vector<attack_type>::iterator itor;
std::unique_ptr<attack_type> dummy_primary;
std::unique_ptr<attack_type> dummy_secondary;
const_attack_itors attacks = u->attacks();
const_attack_itors::const_iterator itor;
attack_ptr dummy_primary;
attack_ptr dummy_secondary;
// death and victory animations are handled here because usually
// the code iterates through all the unit's attacks

View file

@ -522,7 +522,7 @@ unit::unit(const config &cfg, bool use_traits, const vconfig* vcfg)
if(config::const_child_itors cfg_range = cfg.child_range("attack")) {
attacks_.clear();
for (const config& cfg : cfg_range) {
attacks_.push_back(attack_type(cfg));
attacks_.emplace_back(new attack_type(cfg));
}
}
@ -968,7 +968,10 @@ void unit::advance_to(const unit_type &u_type,
jamming_ = new_type.jamming();
movement_type_ = new_type.movement_type();
emit_zoc_ = new_type.has_zoc();
attacks_ = new_type.attacks();
attacks_.clear();
std::transform(new_type.attacks().begin(), new_type.attacks().end(), std::back_inserter(attacks_), [](const attack_type& atk) {
return new attack_type(atk);
});
unit_value_ = new_type.cost();
max_attacks_ = new_type.max_attacks();
@ -1466,7 +1469,7 @@ void unit::write(config& cfg) const
cfg["max_attacks"] = max_attacks_;
cfg["zoc"] = emit_zoc_;
cfg.clear_children("attack");
for(std::vector<attack_type>::const_iterator i = attacks_.begin(); i != attacks_.end(); ++i) {
for(attack_ptr i : attacks_) {
i->write(cfg.add_child("attack"));
}
cfg["cost"] = unit_value_;
@ -1718,8 +1721,7 @@ std::string unit::describe_builtin_effect(std::string apply_to, const config& ef
bool first_attack = true;
std::string desc;
for(std::vector<attack_type>::iterator a = attacks_.begin();
a != attacks_.end(); ++a) {
for(attack_ptr a : attacks_) {
bool affected = a->describe_modification(effect, &desc);
if(affected && desc != "") {
if(first_attack) {
@ -1814,19 +1816,14 @@ void unit::apply_builtin_effect(std::string apply_to, const config& effect)
description_ = *v;
}
} else if(apply_to == "new_attack") {
attacks_.push_back(attack_type(effect));
attacks_.emplace_back(new attack_type(effect));
} else if(apply_to == "remove_attacks") {
std::vector<attack_type>::iterator a = attacks_.begin();
while(a != attacks_.end()) {
if(a->matches_filter(effect)) {
a = attacks_.erase(a);
continue;
}
++a;
}
auto iter = std::remove_if(attacks_.begin(), attacks_.end(), [&effect](attack_ptr a){
return a->matches_filter(effect);
});
attacks_.erase(iter, attacks_.end());
} else if(apply_to == "attack") {
for(std::vector<attack_type>::iterator a = attacks_.begin();
a != attacks_.end(); ++a) {
for(attack_ptr a : attacks_) {
a->apply_modification(effect);
}
} else if(apply_to == "hitpoints") {

View file

@ -252,9 +252,8 @@ public:
void set_emit_zoc(bool val) { emit_zoc_ = val; }
bool get_emit_zoc() const { return emit_zoc_; }
const std::vector<attack_type>& attacks() const { return attacks_; }
std::vector<attack_type>& attacks() { return attacks_; }
const_attack_itors attacks() const { return make_attack_itors(attacks_); }
attack_itors attacks() { return make_attack_itors(attacks_); }
int damage_from(const attack_type& attack,bool attacker,const map_location& loc) const { return resistance_against(attack,attacker,loc); }
@ -483,7 +482,7 @@ private:
std::vector<std::string> overlays_;
std::string role_;
std::vector<attack_type> attacks_;
attack_list attacks_;
protected:
mutable map_location::DIRECTION facing_; //TODO: I think we actually consider this to be part of the gamestate, so it might be better if it's not mutable
//But it's not easy to separate this guy from the animation code right now.