Modify 'apply_to' in an ability filter to do an inclusion check for resistance abilities

Instead of needing to contain the exact string from the ability, 'apply_to' in the filter is now taken as a comma-separated list, where all elements need to be present in the ability.
For example if we filter for apply_to=fire,arcane given an ability [resistance]apply_to=arcane, fire, cold, it will match.
For an ability [resistance]apply_to=arcane, impact, cold, this will not match because 'fire' is missing.
This commit is contained in:
newfrenchy83 2024-06-07 18:08:43 +02:00 committed by GitHub
parent 2c13a67fa6
commit 1809af8cdf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 134 additions and 2 deletions

View File

@ -0,0 +1,2 @@
### WML Engine
* modify 'apply_to' in [experimental_filter_ability(_active)] or [overwrite][experimental_filter_specials] to do an inclusion check for a comma-separated list of damage types in [resistance] abilities.

View File

@ -514,3 +514,97 @@
)}
#undef FILTER_ABILITY_TEST_WITHOUT_VALUE
#####
# API(s) being tested: [event][filter][experimental_filter_ability]apply_to= when ability checked is [resistance]
##
# Actions:
# Use the common setup from FILTER_ABILITY_RESISTANCE_TEST.
# Add an event with a filter matching Alice's resistance ability applied to arcane, cold and fire damage.
# Alice attacks Bob and then Bob attacks Alice, as defined in FILTER_ABILITY_RESISTANCE_TEST.
##
# Expected end state:
# The filtered event is triggered exactly once.
#####
{GENERIC_UNIT_TEST event_test_filter_ability_apply_to_resistance (
[event]
name=start
# Make sure the attacks hit
{FORCE_CHANCE_TO_HIT (id=bob) (id=alice) 100 ()}
{FORCE_CHANCE_TO_HIT (id=alice) (id=bob) 100 ()}
[modify_unit]
[filter]
[/filter]
# Make sure they don't die during the attacks
[status]
invulnerable=yes
[/status]
[/modify_unit]
[object]
silent=yes
[effect]
apply_to=new_ability
[abilities]
[resistance]
value=50
apply_to=arcane,cold,fire
[/resistance]
[/abilities]
[/effect]
[filter]
id=alice
[/filter]
[/object]
{VARIABLE triggers 0}
[/event]
[event]
name=side 1 turn 1
[do_command]
[move]
x=7,13
y=3,4
[/move]
[attack]
[source]
x,y=13,4
[/source]
[destination]
x,y=13,3
[/destination]
[/attack]
[/do_command]
[end_turn][/end_turn]
[/event]
[event]
name=side 2 turn
[do_command]
[attack]
[source]
x,y=13,3
[/source]
[destination]
x,y=13,4
[/destination]
[/attack]
[/do_command]
[end_turn][/end_turn]
[/event]
[event]
name=attack
first_time_only=no
[filter]
[experimental_filter_ability]
tag_name=resistance
apply_to=arcane,fire
[/experimental_filter_ability]
[/filter]
{ASSERT ({VARIABLE_CONDITIONAL side_number equals 1})}
{VARIABLE_OP triggers add 1}
[/event]
[event]
name=turn 2
{RETURN ({VARIABLE_CONDITIONAL triggers equals 1})}
[/event]
)}

View File

@ -1472,8 +1472,15 @@ static bool matches_ability_filter(const config & cfg, const std::string& tag_na
if(!string_matches_if_present(filter, cfg, "id", ""))
return false;
if(!string_matches_if_present(filter, cfg, "apply_to", "self"))
return false;
if(tag_name == "resistance"){
if(!set_includes_if_present(filter, cfg, "apply_to")){
return false;
}
} else {
if(!string_matches_if_present(filter, cfg, "apply_to", "self")){
return false;
}
}
if(!string_matches_if_present(filter, cfg, "overwrite_specials", "none"))
return false;

View File

@ -13,6 +13,7 @@
*/
#include <algorithm>
#include <set>
#include <vector>
#include "utils/config_filters.hpp"
@ -41,6 +42,26 @@ bool utils::config_filters::string_matches_if_present(
std::find(filter_attribute.begin(), filter_attribute.end(), cfg[attribute].str(def)) != filter_attribute.end());
}
bool utils::config_filters::set_includes_if_present(const config& filter, const config& cfg, const std::string& attribute)
{
if(!filter.has_attribute(attribute)) {
return true;
}
if(!cfg.has_attribute(attribute)) {
return false;
}
const std::set<std::string> filter_attribute = utils::split_set(filter[attribute].str());
const std::set<std::string> cfg_attribute = utils::split_set(cfg[attribute].str());
for(const std::string& fil_at : filter_attribute) {
if (cfg_attribute.count(fil_at) == 0){
return false;
}
}
return true;
}
bool utils::config_filters::unsigned_matches_if_present(const config& filter, const config& cfg, const std::string& attribute)
{
if(!filter.has_attribute(attribute)) {

View File

@ -67,6 +67,13 @@ bool int_matches_if_present_or_negative(
bool string_matches_if_present(
const config& filter, const config& cfg, const std::string& attribute, const std::string& def);
/**
* filter[attribute] and cfg[attribute] are assumed to be comma-separated lists.
* If the filter is present, each item in filter[attribute] must match an item in cfg[attribute]
* for the function to return true.
*/
bool set_includes_if_present(const config& filter, const config& cfg, const std::string& attribute);
bool bool_or_empty(const config& filter, const config& cfg, const std::string& attribute);
} // namespace utils::config_filters

View File

@ -155,6 +155,7 @@
0 event_test_filter_ability_active_inactive
0 event_test_filter_ability_with_value_by_default
0 event_test_filter_ability_no_match_by_default
0 event_test_filter_ability_apply_to_resistance
0 test_ability_id_active
0 test_ability_id_not_active
0 event_test_filter_attack