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:
parent
496a523fa2
commit
b143ea4397
17 changed files with 96 additions and 110 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue