Skip to content

Commit

Permalink
use the tag_name of each special to avoid infinite loops.
Browse files Browse the repository at this point in the history
With this method, less cumbersome than the previous proposal that I removed, the check works perfectly for special weapons and for the event filter while the proposal to block at the level of the second loop creates a gap between the two cases, we could had a special train active but the filter_attack did not match or vice versa.

I arranged so that the tag_name is only used for example for [filter_self] when applied to self, so the type of a modified opponent can be detected by a [filter_opponent] of a [damage] applied to self.
  • Loading branch information
newfrenchy83 committed Nov 3, 2023
1 parent dd40338 commit d658ffa
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 19 deletions.
21 changes: 13 additions & 8 deletions src/units/abilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ bool unit::ability_affects_weapon(const config& cfg, const_attack_ptr weapon, bo
if(!weapon) {
return false;
}
return weapon->matches_filter(filter, true);
return weapon->matches_filter(filter);
}

bool unit::has_ability_type(const std::string& ability) const
Expand Down Expand Up @@ -1276,19 +1276,20 @@ namespace { // Helpers for attack_type::special_active()
* (normally a [filter_*] child) of the provided filter.
* @param[in] u A unit to filter.
* @param[in] u2 Another unit to filter.
* @param[in] loc The presumed location of @a un_it.
* @param[in] loc The presumed location of @a unit.
* @param[in] weapon The attack_type to filter.
* @param[in] filter The filter containing the child filter to use.
* @param[in] for_listing
* @param[in] child_tag The tag of the child filter to use.
* @param[in] tag_name Parameter used for don't have infinite recusion for some filter attribute.
*/
static bool special_unit_matches(unit_const_ptr & u,
unit_const_ptr & u2,
const map_location & loc,
const_attack_ptr weapon,
const config & filter,
const bool for_listing,
const std::string & child_tag)
const std::string & child_tag, const std::string& tag_name)
{
if (for_listing && !loc.valid())
// The special's context was set to ignore this unit, so assume we pass.
Expand All @@ -1315,7 +1316,7 @@ namespace { // Helpers for attack_type::special_active()

// Check for a weapon match.
if (auto filter_weapon = filter_child->optional_child("filter_weapon") ) {
if ( !weapon || !weapon->matches_filter(*filter_weapon, true) )
if ( !weapon || !weapon->matches_filter(*filter_weapon, tag_name) )
return false;
}

Expand Down Expand Up @@ -1813,13 +1814,17 @@ bool attack_type::special_active_impl(
const config& special_backstab = special["backstab"].to_bool() ? cfg : special;

// Filter the units involved.
if (!special_unit_matches(self, other, self_loc, self_attack, special, is_for_listing, filter_self))
std::string self_tag_name = whom_is_self ? tag_name : "";
std::string opp_tag_name = !whom_is_self ? tag_name : "";
std::string att_tag_name = is_attacker ? tag_name : "";
std::string def_tag_name = !is_attacker ? tag_name : "";
if (!special_unit_matches(self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_tag_name))
return false;
if (!special_unit_matches(other, self, other_loc, other_attack, special_backstab, is_for_listing, "filter_opponent"))
if (!special_unit_matches(other, self, other_loc, other_attack, special_backstab, is_for_listing, "filter_opponent", opp_tag_name))
return false;
if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing, "filter_attacker"))
if (!special_unit_matches(att, def, att_loc, att_weapon, special, is_for_listing, "filter_attacker", att_tag_name))
return false;
if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing, "filter_defender"))
if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing, "filter_defender", def_tag_name))
return false;

const auto adjacent = get_adjacent_tiles(self_loc);
Expand Down
23 changes: 13 additions & 10 deletions src/units/attack_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,15 @@ std::string attack_type::accuracy_parry_description() const
return s.str();
}

static bool check_string(const std::vector<std::string>& filter_attribute, const std::string& attribute)
{
return (std::find(filter_attribute.begin(), filter_attribute.end(), attribute) != filter_attribute.end());
}
/**
* Returns whether or not *this matches the given @a filter, ignoring the
* complexities introduced by [and], [or], and [not].
*/
static bool matches_simple_filter(const attack_type & attack, const config & filter, bool no_check)
static bool matches_simple_filter(const attack_type & attack, const config & filter, const std::string& tag_name)
{
const std::vector<std::string>& filter_range = utils::split(filter["range"]);
const std::string& filter_damage = filter["damage"];
Expand Down Expand Up @@ -145,13 +149,12 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
return false;

if (!filter_type.empty()){
if(no_check){
if (std::find(filter_type.begin(), filter_type.end(), attack.type()) == filter_type.end() ){
if(tag_name == "damage"){
if (!check_string(filter_type, attack.type())){
return false;
}
} else {
std::pair<std::string, std::string> damage_type = attack.damage_type();
if ((std::find(filter_type.begin(), filter_type.end(), damage_type.first) == filter_type.end()) && (std::find(filter_type.begin(), filter_type.end(), damage_type.second) == filter_type.end())){
if (!check_string(filter_type, attack.type()) && !check_string(filter_type, attack.alternative_type())){
return false;
}
}
Expand Down Expand Up @@ -255,25 +258,25 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
/**
* Returns whether or not *this matches the given @a filter.
*/
bool attack_type::matches_filter(const config& filter, bool no_check) const
bool attack_type::matches_filter(const config& filter, const std::string& tag_name) const
{
// Handle the basic filter.
bool matches = matches_simple_filter(*this, filter, no_check);
bool matches = matches_simple_filter(*this, filter, tag_name);

// Handle [and], [or], and [not] with in-order precedence
for (const config::any_child condition : filter.all_children_range() )
{
// Handle [and]
if ( condition.key == "and" )
matches = matches && matches_filter(condition.cfg, no_check);
matches = matches && matches_filter(condition.cfg, tag_name);

// Handle [or]
else if ( condition.key == "or" )
matches = matches || matches_filter(condition.cfg, no_check);
matches = matches || matches_filter(condition.cfg, tag_name);

// Handle [not]
else if ( condition.key == "not" )
matches = matches && !matches_filter(condition.cfg, no_check);
matches = matches && !matches_filter(condition.cfg, tag_name);
}

return matches;
Expand Down
2 changes: 1 addition & 1 deletion src/units/attack_type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class attack_type : public std::enable_shared_from_this<attack_type>

// In unit_types.cpp:

bool matches_filter(const config& filter, bool no_check = false) const;
bool matches_filter(const config& filter, const std::string& tag_name = "") const;
bool apply_modification(const config& cfg);
bool describe_modification(const config& cfg,std::string* description);

Expand Down

0 comments on commit d658ffa

Please sign in to comment.