[1.17] add [filter_ability] in [filter] events and [effect]remove_ability and [filter_ability_active] in [filter] events
see https://forums.wesnoth.org/viewtopic.php?p=681371#p681371 in forum the type of ability used is also filtered.
This commit is contained in:
parent
e9c9bb62d5
commit
f9a94e1312
11 changed files with 630 additions and 1 deletions
|
@ -0,0 +1,2 @@
|
|||
### WML Engine
|
||||
* Add [filter_ability] usable instead of [abilities][tag name] to filter attributes including the type of ability used.
|
21
data/schema/filters/abilities.cfg
Normal file
21
data/schema/filters/abilities.cfg
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
[tag]
|
||||
name="$filter_abilities"
|
||||
max=0
|
||||
{SIMPLE_KEY id string_list}
|
||||
{SIMPLE_KEY tag_name string_list}
|
||||
{SIMPLE_KEY overwrite_specials string_list}
|
||||
{SIMPLE_KEY apply_to string_list}
|
||||
{SIMPLE_KEY active_on string_list}
|
||||
{SIMPLE_KEY value s_range_list}
|
||||
{SIMPLE_KEY add s_range_list}
|
||||
{SIMPLE_KEY sub s_range_list}
|
||||
{SIMPLE_KEY multiply s_real_range_list}
|
||||
{SIMPLE_KEY divide s_real_range_list}
|
||||
{SIMPLE_KEY affect_adjacent s_bool}
|
||||
{SIMPLE_KEY affect_self s_bool}
|
||||
{SIMPLE_KEY affect_allies s_bool}
|
||||
{SIMPLE_KEY affect_enemies s_bool}
|
||||
{DEFAULT_KEY type_value value_type empty}
|
||||
{FILTER_BOOLEAN_OPS abilities}
|
||||
[/tag]
|
|
@ -46,6 +46,8 @@
|
|||
{FILTER_TAG "filter_adjacent" adjacent ()}
|
||||
{FILTER_TAG "filter_location" location ()}
|
||||
{FILTER_TAG "filter_side" side ()}
|
||||
{FILTER_TAG "filter_ability" abilities ()}
|
||||
{FILTER_TAG "filter_ability_active" abilities ()}
|
||||
{FILTER_BOOLEAN_OPS unit}
|
||||
[/tag]
|
||||
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
name="ability_overwrite"
|
||||
value="none|one_side|both_sides"
|
||||
[/type]
|
||||
[type]
|
||||
name="value_type"
|
||||
value="empty|value|add|sub|multiply|divide"
|
||||
[/type]
|
||||
[type]
|
||||
name="addon_type"
|
||||
value="sp|mp|hybrid"
|
||||
|
@ -88,6 +92,18 @@
|
|||
value="\d+-infinity"
|
||||
[/element]
|
||||
)}
|
||||
# definition of real_range_list and s_real_range_list, before macros are expanded
|
||||
{LIST_TYPE_COMPLEX real_range (
|
||||
[element]
|
||||
value="\d+(\.\d+)?"
|
||||
[/element]
|
||||
[element]
|
||||
value="\d+(\.\d+)?-\d+(\.\d+)?"
|
||||
[/element]
|
||||
[element]
|
||||
value="\d+(\.\d+)?-infinity"
|
||||
[/element]
|
||||
)}
|
||||
[type]
|
||||
name=alignment
|
||||
value="lawful|neutral|chaotic|liminal"
|
||||
|
@ -375,6 +391,7 @@
|
|||
{SUBST_TYPE coordinate}
|
||||
{SUBST_TYPE coordinates}
|
||||
{SUBST_TYPE range_list}
|
||||
{SUBST_TYPE real_range_list}
|
||||
{SUBST_TYPE terrain_code}
|
||||
{SUBST_TYPE terrain_list}
|
||||
{SUBST_TYPE dir}
|
||||
|
|
|
@ -140,9 +140,14 @@
|
|||
[/tag]
|
||||
[/case]
|
||||
[case]
|
||||
value=new_ability,remove_ability
|
||||
value=new_ability
|
||||
{LINK_TAG "units/unit_type/abilities"}
|
||||
[/case]
|
||||
[case]
|
||||
value=remove_ability
|
||||
{LINK_TAG "units/unit_type/abilities"}
|
||||
{FILTER_TAG "filter_ability" abilities ()}
|
||||
[/case]
|
||||
[case]
|
||||
value=new_animation
|
||||
{SIMPLE_KEY id string}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
#####
|
||||
# API(s) being tested: [effect]apply_to=remove_ability[filter_ability]
|
||||
##
|
||||
# Actions:
|
||||
# Two [chance_to_hit] abilities are added, setting the chance to 0 and 100 respectively.
|
||||
# The one setting the chance to 100 is removed, filtering by id and tag_name.
|
||||
# Have alice attack bob.
|
||||
##
|
||||
# Expected end state:
|
||||
# All strikes for both units miss.
|
||||
#####
|
||||
{GENERIC_UNIT_TEST "test_remove_ability_by_filter" (
|
||||
[event]
|
||||
name=start
|
||||
[modify_unit]
|
||||
[filter]
|
||||
[/filter]
|
||||
max_hitpoints=100
|
||||
hitpoints=100
|
||||
attacks_left=1
|
||||
[/modify_unit]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[chance_to_hit]
|
||||
id=test_cth_0
|
||||
value=0
|
||||
[/chance_to_hit]
|
||||
[chance_to_hit]
|
||||
id=test_cth
|
||||
value=100
|
||||
[/chance_to_hit]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
[chance_to_hit]
|
||||
id=test_cth_0
|
||||
value=0
|
||||
[/chance_to_hit]
|
||||
[chance_to_hit]
|
||||
id=test_cth
|
||||
value=100
|
||||
[/chance_to_hit]
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
[/object]
|
||||
[object]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=remove_ability
|
||||
[filter_ability]
|
||||
id=test_cth
|
||||
tag_name=chance_to_hit
|
||||
[/filter_ability]
|
||||
[/effect]
|
||||
[/object]
|
||||
[object]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=remove_ability
|
||||
[filter_ability]
|
||||
id=test_cth
|
||||
tag_name=chance_to_hit
|
||||
[/filter_ability]
|
||||
[/effect]
|
||||
[/object]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
kill=yes
|
||||
[/store_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=bob
|
||||
[/filter]
|
||||
variable=b
|
||||
[/store_unit]
|
||||
[unstore_unit]
|
||||
variable=a
|
||||
find_vacant=yes
|
||||
x,y=$b.x,$b.y
|
||||
[/unstore_unit]
|
||||
[store_unit]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
variable=a
|
||||
[/store_unit]
|
||||
|
||||
[do_command]
|
||||
[attack]
|
||||
weapon=0
|
||||
defender_weapon=0
|
||||
[source]
|
||||
x,y=$a.x,$a.y
|
||||
[/source]
|
||||
[destination]
|
||||
x,y=$b.x,$b.y
|
||||
[/destination]
|
||||
[/attack]
|
||||
[/do_command]
|
||||
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
|
||||
[event]
|
||||
name=attacker hits,defender hits
|
||||
first_time_only=no
|
||||
{FAIL}
|
||||
[/event]
|
||||
)}
|
|
@ -0,0 +1,219 @@
|
|||
# wmllint: no translatables
|
||||
|
||||
##
|
||||
# Common code for the tests in this file.
|
||||
##
|
||||
# Actions:
|
||||
# Give both Alice and Bob 100% chance to hit.
|
||||
# Give Alice a drains ability which is only active during liminal times of day.
|
||||
# During Alice's turn, move Alice next to Bob, and have Alice attack Bob.
|
||||
# During Bob's turn, have Bob attack Alice.
|
||||
##
|
||||
#define FILTER_ABILITY_TEST
|
||||
[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]
|
||||
[drains]
|
||||
value=25
|
||||
[filter]
|
||||
[filter_location]
|
||||
time_of_day=neutral
|
||||
[/filter_location]
|
||||
[/filter]
|
||||
[/drains]
|
||||
[/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]
|
||||
#enddef
|
||||
|
||||
#####
|
||||
# API(s) being tested: [event][filter][filter_ability]
|
||||
##
|
||||
# Actions:
|
||||
# Use the common setup from FILTER_ABILITY_TEST.
|
||||
# Add an event with a filter matching Alice's drains ability.
|
||||
# Alice attacks Bob and then Bob attacks Alice, as defined in FILTER_ABILITY_TEST.
|
||||
##
|
||||
# Expected end state:
|
||||
# The filtered event is triggered exactly once.
|
||||
#####
|
||||
{GENERIC_UNIT_TEST event_test_filter_ability (
|
||||
{FILTER_ABILITY_TEST}
|
||||
[event]
|
||||
name=attack
|
||||
[filter]
|
||||
[filter_ability]
|
||||
tag_name=drains
|
||||
value=25
|
||||
[/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]
|
||||
)}
|
||||
|
||||
#####
|
||||
# API(s) being tested: [event][filter][filter_ability]
|
||||
##
|
||||
# Actions:
|
||||
# Use the common setup from FILTER_ABILITY_TEST.
|
||||
# Add an event with a filter for a drains ability, but a different value to Alice's ability.
|
||||
# Alice attacks Bob and then Bob attacks Alice, as defined in FILTER_ABILITY_TEST.
|
||||
##
|
||||
# Expected end state:
|
||||
# The filtered event is never triggered.
|
||||
#####
|
||||
{GENERIC_UNIT_TEST event_test_filter_ability_no_match (
|
||||
{FILTER_ABILITY_TEST}
|
||||
[event]
|
||||
name=attack
|
||||
[filter]
|
||||
[filter_ability]
|
||||
tag_name=drains
|
||||
value=45
|
||||
[/filter_ability]
|
||||
[/filter]
|
||||
{FAIL}
|
||||
[/event]
|
||||
[event]
|
||||
name=turn 2
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
#####
|
||||
# API(s) being tested: [event][filter][filter_ability_active]
|
||||
##
|
||||
# Actions:
|
||||
# Use the common setup from FILTER_ABILITY_TEST.
|
||||
# Add an event with a filter matching Alice's drains ability, but only when it's active.
|
||||
# Alice attacks Bob and then Bob attacks Alice, as defined in FILTER_ABILITY_TEST.
|
||||
##
|
||||
# Expected end state:
|
||||
# The filtered event is triggered exactly once.
|
||||
#####
|
||||
{GENERIC_UNIT_TEST event_test_filter_ability_active (
|
||||
{FILTER_ABILITY_TEST}
|
||||
[event]
|
||||
name=attack
|
||||
[filter]
|
||||
[filter_ability_active]
|
||||
tag_name=drains
|
||||
value=25
|
||||
[/filter_ability_active]
|
||||
[/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]
|
||||
)}
|
||||
|
||||
#####
|
||||
# API(s) being tested: [event][filter][filter_ability_active]
|
||||
##
|
||||
# Actions:
|
||||
# Use the common setup from FILTER_ABILITY_TEST.
|
||||
# Add an event with a filter matching Alice's drains ability, but only when it's active.
|
||||
# Give Alice the Illuminates ability, which makes it the wrong time of day for her drains ability.
|
||||
# The events in FILTER_ABILITY_TEST make Alice attacks Bob and then Bob attack Alice.
|
||||
##
|
||||
# Expected end state:
|
||||
# The filtered event is never triggered.
|
||||
#####
|
||||
{GENERIC_UNIT_TEST event_test_filter_ability_active_inactive (
|
||||
{FILTER_ABILITY_TEST}
|
||||
[event]
|
||||
name=start
|
||||
[object]
|
||||
silent=yes
|
||||
[effect]
|
||||
apply_to=new_ability
|
||||
[abilities]
|
||||
{ABILITY_ILLUMINATES}
|
||||
[/abilities]
|
||||
[/effect]
|
||||
[filter]
|
||||
id=alice
|
||||
[/filter]
|
||||
[/object]
|
||||
[/event]
|
||||
|
||||
[event]
|
||||
name=attack
|
||||
[filter]
|
||||
[filter_ability_active]
|
||||
tag_name=drains
|
||||
value=25
|
||||
[/filter_ability_active]
|
||||
[/filter]
|
||||
{FAIL}
|
||||
[/event]
|
||||
|
||||
[event]
|
||||
name=turn 2
|
||||
{SUCCEED}
|
||||
[/event]
|
||||
)}
|
||||
|
||||
#undef FILTER_ABILITY_TEST
|
|
@ -806,6 +806,49 @@ void unit_filter_compound::fill(vconfig cfg)
|
|||
return side_filter(c, args.fc).match(args.u.side());
|
||||
});
|
||||
}
|
||||
else if (child.first == "filter_ability") {
|
||||
create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
|
||||
for(const config::any_child ab : args.u.abilities().all_children_range()) {
|
||||
if(args.u.ability_matches_filter(ab.cfg, ab.key, c.get_parsed_config())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else if (child.first == "filter_ability_active") {
|
||||
create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
|
||||
if(!display::get_singleton()){
|
||||
return false;
|
||||
}
|
||||
const unit_map& units = display::get_singleton()->get_units();
|
||||
for(const config::any_child ab : args.u.abilities().all_children_range()) {
|
||||
if(args.u.ability_matches_filter(ab.cfg, ab.key, c.get_parsed_config())) {
|
||||
if (args.u.get_self_ability_bool(ab.cfg, ab.key, args.loc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto adjacent = get_adjacent_tiles(args.loc);
|
||||
for(unsigned i = 0; i < adjacent.size(); ++i) {
|
||||
const unit_map::const_iterator it = units.find(adjacent[i]);
|
||||
if (it == units.end() || it->incapacitated())
|
||||
continue;
|
||||
if (&*it == (args.u.shared_from_this()).get())
|
||||
continue;
|
||||
|
||||
for(const config::any_child ab : it->abilities().all_children_range()) {
|
||||
if(it->ability_matches_filter(ab.cfg, ab.key, c.get_parsed_config())) {
|
||||
if (args.u.get_adj_ability_bool(ab.cfg, ab.key, i, args.loc, *it)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
else if (child.first == "has_attack") {
|
||||
create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
|
||||
for(const attack_type& a : args.u.attacks()) {
|
||||
|
|
|
@ -1420,6 +1420,171 @@ void unit::remove_ability_by_id(const std::string& ability)
|
|||
}
|
||||
}
|
||||
|
||||
static bool bool_matches_if_present(const config& filter, const config& cfg, const std::string& attribute, bool def)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return filter[attribute].to_bool() == cfg[attribute].to_bool(def);
|
||||
}
|
||||
|
||||
static bool string_matches_if_present(const config& filter, const config& cfg, const std::string& attribute, const std::string& def)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<std::string> filter_attribute = utils::split(filter[attribute]);
|
||||
return ( std::find(filter_attribute.begin(), filter_attribute.end(), cfg[attribute].str(def)) != filter_attribute.end() );
|
||||
}
|
||||
|
||||
static bool int_matches_if_present(const config& filter, const config& cfg, const std::string& attribute)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(cfg[attribute].empty() && (attribute == "add" || attribute == "sub")){
|
||||
if(attribute == "add"){
|
||||
return in_ranges<int>(-cfg["sub"].to_int(0), utils::parse_ranges(filter[attribute].str()));
|
||||
} else if(attribute == "sub"){
|
||||
return in_ranges<int>(-cfg["add"].to_int(0), utils::parse_ranges(filter[attribute].str()));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return in_ranges<int>(cfg[attribute].to_int(0), utils::parse_ranges(filter[attribute].str()));
|
||||
}
|
||||
|
||||
static bool double_matches_if_present(const config& filter, const config& cfg, const std::string& attribute)
|
||||
{
|
||||
if(filter[attribute].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_ranges<double>(cfg[attribute].to_double(1), utils::parse_ranges_real(filter[attribute].str()));
|
||||
}
|
||||
|
||||
static bool type_value_if_present(const config& filter, const config& cfg)
|
||||
{
|
||||
if(filter["type_value"].empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string cfg_type_value;
|
||||
const std::vector<std::string> filter_attribute = utils::split(filter["type_value"]);
|
||||
if(!cfg["value"].empty()){
|
||||
cfg_type_value ="value";
|
||||
} else if(!cfg["add"].empty()){
|
||||
cfg_type_value ="add";
|
||||
} else if(!cfg["sub"].empty()){
|
||||
cfg_type_value ="sub";
|
||||
} else if(!cfg["multiply"].empty()){
|
||||
cfg_type_value ="multiply";
|
||||
} else if(!cfg["divide"].empty()){
|
||||
cfg_type_value ="divide";
|
||||
}
|
||||
return ( std::find(filter_attribute.begin(), filter_attribute.end(), cfg_type_value) != filter_attribute.end() );
|
||||
}
|
||||
|
||||
static bool matches_ability_filter(const config & cfg, const std::string& tag_name, const config & filter)
|
||||
{
|
||||
if(!filter["affect_adjacent"].empty()){
|
||||
auto cfg_adjacent = cfg.optional_child("affect_adjacent");
|
||||
bool adjacent = cfg_adjacent ? true : false;;
|
||||
if(filter["affect_adjacent"].to_bool() != adjacent){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!bool_matches_if_present(filter, cfg, "affect_self", true))
|
||||
return false;
|
||||
|
||||
if(!bool_matches_if_present(filter, cfg, "affect_allies", true))
|
||||
return false;
|
||||
|
||||
if(!bool_matches_if_present(filter, cfg, "affect_enemies", false))
|
||||
return false;
|
||||
|
||||
if(!bool_matches_if_present(filter, cfg, "cumulative", false))
|
||||
return false;
|
||||
|
||||
const std::vector<std::string> filter_type = utils::split(filter["tag_name"]);
|
||||
if ( !filter_type.empty() && std::find(filter_type.begin(), filter_type.end(), tag_name) == filter_type.end() )
|
||||
return false;
|
||||
|
||||
if(!string_matches_if_present(filter, cfg, "id", ""))
|
||||
return false;
|
||||
|
||||
if(!string_matches_if_present(filter, cfg, "apply_to", "self"))
|
||||
return false;
|
||||
|
||||
if(!string_matches_if_present(filter, cfg, "overwrite_specials", "none"))
|
||||
return false;
|
||||
|
||||
if(!string_matches_if_present(filter, cfg, "active_on", "both"))
|
||||
return false;
|
||||
|
||||
if(!int_matches_if_present(filter, cfg, "value"))
|
||||
return false;
|
||||
|
||||
if(!int_matches_if_present(filter, cfg, "add"))
|
||||
return false;
|
||||
|
||||
if(!int_matches_if_present(filter, cfg, "sub"))
|
||||
return false;
|
||||
|
||||
if(!double_matches_if_present(filter, cfg, "multiply"))
|
||||
return false;
|
||||
|
||||
if(!double_matches_if_present(filter, cfg, "divide"))
|
||||
return false;
|
||||
|
||||
if(!type_value_if_present(filter, cfg))
|
||||
return false;
|
||||
|
||||
// Passed all tests.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unit::ability_matches_filter(const config & cfg, const std::string& tag_name, const config & filter) const
|
||||
{
|
||||
// Handle the basic filter.
|
||||
bool matches = matches_ability_filter(cfg, tag_name, filter);
|
||||
|
||||
// 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 && ability_matches_filter(cfg, tag_name, condition.cfg);
|
||||
|
||||
// Handle [or]
|
||||
else if ( condition.key == "or" )
|
||||
matches = matches || ability_matches_filter(cfg, tag_name, condition.cfg);
|
||||
|
||||
// Handle [not]
|
||||
else if ( condition.key == "not" )
|
||||
matches = matches && !ability_matches_filter(cfg, tag_name, condition.cfg);
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
void unit::remove_ability_by_attribute(const config& filter)
|
||||
{
|
||||
set_attr_changed(UA_ABILITIES);
|
||||
config::all_children_iterator i = abilities_.ordered_begin();
|
||||
while (i != abilities_.ordered_end()) {
|
||||
if(ability_matches_filter(i->cfg, i->key, filter)) {
|
||||
i = abilities_.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool unit::get_attacks_changed() const
|
||||
{
|
||||
for(const auto& a_ptr : attacks_) {
|
||||
|
@ -2127,10 +2292,14 @@ void unit::apply_builtin_effect(std::string apply_to, const config& effect)
|
|||
}
|
||||
} else if(apply_to == "remove_ability") {
|
||||
if(auto ab_effect = effect.optional_child("abilities")) {
|
||||
deprecated_message("[effect]apply_to=remove_ability [abilities]", DEP_LEVEL::INDEFINITE, "", "Use [filter_ability] instead in [effect]apply_to=remove_ability");
|
||||
for(const config::any_child ab : ab_effect->all_children_range()) {
|
||||
remove_ability_by_id(ab.cfg["id"]);
|
||||
}
|
||||
}
|
||||
if(auto fab_effect = effect.optional_child("filter_ability")) {
|
||||
remove_ability_by_attribute(*fab_effect);
|
||||
}
|
||||
} else if(apply_to == "image_mod") {
|
||||
LOG_UT << "applying image_mod";
|
||||
std::string mod = effect["replace"];
|
||||
|
|
|
@ -1772,6 +1772,20 @@ public:
|
|||
*/
|
||||
void remove_ability_by_id(const std::string& ability);
|
||||
|
||||
/**
|
||||
* Removes a unit's abilities with a specific ID or other attribute.
|
||||
* @param filter the config of ability to remove.
|
||||
*/
|
||||
void remove_ability_by_attribute(const config& filter);
|
||||
|
||||
/**
|
||||
* Verify what abilities attributes match with filter.
|
||||
* @param cfg the config of ability to check.
|
||||
* @param tag_name the tag name of ability to check.
|
||||
* @param filter the filter used for checking.
|
||||
*/
|
||||
bool ability_matches_filter(const config & cfg, const std::string& tag_name, const config & filter) const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -142,6 +142,10 @@
|
|||
0 event_test_filter_condition
|
||||
0 event_test_filter_side
|
||||
0 event_test_filter_unit
|
||||
0 event_test_filter_ability
|
||||
0 event_test_filter_ability_no_match
|
||||
0 event_test_filter_ability_active
|
||||
0 event_test_filter_ability_active_inactive
|
||||
0 test_ability_id_active
|
||||
0 test_ability_id_not_active
|
||||
0 event_test_filter_attack
|
||||
|
@ -320,6 +324,7 @@
|
|||
0 test_force_chance_to_hit_macro
|
||||
0 trait_exclusion_test
|
||||
0 trait_requirement_test
|
||||
0 test_remove_ability_by_filter
|
||||
0 swarms_filter_student_by_type
|
||||
0 swarms_effects_not_checkable
|
||||
0 filter_special_id_active
|
||||
|
|
Loading…
Add table
Reference in a new issue