diff --git a/data/schema/units/abilities.cfg b/data/schema/units/abilities.cfg index 785e03c4d3a..7caf6fc0a60 100644 --- a/data/schema/units/abilities.cfg +++ b/data/schema/units/abilities.cfg @@ -5,6 +5,8 @@ max=infinite super="units/unit_type/abilities/~generic~,units/unit_type/attack/specials/" + {NAME} {FILTER_TAG "filter_student" unit {FILTER_TAG "filter_weapon" weapon ()}} + {FILTER_TAG "filter_adjacent_student" adjacent ()} + {FILTER_TAG "filter_adjacent_student_location" adjacent_location ()} [/tag] #enddef diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_active.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_active.cfg new file mode 100644 index 00000000000..2abffc32160 --- /dev/null +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_active.cfg @@ -0,0 +1,49 @@ +#textdomain wesnoth-test + +##### +# API(s) being tested: ability[filter_adjacent]adjacent,count= +## +# Actions: +# Give Alice an ability specialX, which is only active if one adjacent unit is bob. +# Test whether the ability is active. +## +# Expected end state: +# specialX should be active. +##### +{COMMON_KEEP_A_B_C_D_UNIT_TEST "filter_adjacent_active" ( + [event] + name=start + + [object] + silent=yes + [effect] + apply_to=new_ability + [abilities] + [damage] + id=specialX + name=_ "specialX" + description=_ "specialX is active if and only if one of the adjacents units is bob" + value=100 + apply_to=self + [filter_adjacent] + adjacent=n,ne,se,s,sw,nw + count=1-6 + id=bob + [/filter_adjacent] + [/damage] + [/abilities] + [/effect] + [filter] + id=alice + [/filter] + [/object] + + {ASSERT ( + [have_unit] + ability_id_active=specialX + [/have_unit] + )} + + {SUCCEED} + [/event] +)} diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_direction_active.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_direction_active.cfg new file mode 100644 index 00000000000..8abdbc096b4 --- /dev/null +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_direction_active.cfg @@ -0,0 +1,48 @@ +#textdomain wesnoth-test + +##### +# API(s) being tested: ability[filter_adjacent]adjacent,count= +## +# Actions: +# Give Alice an ability specialX, which is only active if one adjacent unit is bob, and bob is in correct location. +# Test whether the ability is active. +## +# Expected end state: +# specialX should be active. +##### +{COMMON_KEEP_A_B_C_D_UNIT_TEST "filter_adjacent_direction_active" ( + [event] + name=start + + [object] + silent=yes + [effect] + apply_to=new_ability + [abilities] + [damage] + id=specialX + name=_ "specialX" + description=_ "specialX is active if and only if one of the adjacents units is bob, and bob is in correct location" + value=100 + apply_to=self + [filter_adjacent] + adjacent=se,s,sw + id=bob + [/filter_adjacent] + [/damage] + [/abilities] + [/effect] + [filter] + id=alice + [/filter] + [/object] + + {ASSERT ( + [have_unit] + ability_id_active=specialX + [/have_unit] + )} + + {SUCCEED} + [/event] +)} diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_direction_inactive.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_direction_inactive.cfg new file mode 100644 index 00000000000..c94ba8a1ffe --- /dev/null +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_direction_inactive.cfg @@ -0,0 +1,50 @@ +#textdomain wesnoth-test + +##### +# API(s) being tested: ability[filter_adjacent]adjacent,count= +## +# Actions: +# Give Alice an ability specialX, which is only active if one adjacent unit is bob, and bob is in correct location. +# Test whether the ability is active. +## +# Expected end state: +# specialX shouldn't be active. +##### +{COMMON_KEEP_A_B_C_D_UNIT_TEST "filter_adjacent_direction_inactive" ( + [event] + name=start + + [object] + silent=yes + [effect] + apply_to=new_ability + [abilities] + [damage] + id=specialX + name=_ "specialX" + description=_ "specialX is active if and only if one of the adjacents units is bob, and bob is in correct location" + value=100 + apply_to=self + [filter_adjacent] + adjacent=n,ne,nw + id=bob + [/filter_adjacent] + [/damage] + [/abilities] + [/effect] + [filter] + id=alice + [/filter] + [/object] + + {ASSERT ( + [not] + [have_unit] + ability_id_active=specialX + [/have_unit] + [/not] + )} + + {SUCCEED} + [/event] +)} diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_inactive.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_inactive.cfg new file mode 100644 index 00000000000..ccbbbf5c876 --- /dev/null +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_inactive.cfg @@ -0,0 +1,51 @@ +#textdomain wesnoth-test + +##### +# API(s) being tested: ability[filter_adjacent]adjacent,count= +## +# Actions: +# Give Alice an ability specialX, which is only active if one adjacent unit is Loki. +# Test whether the ability is active. +## +# Expected end state: +# specialX shouldn't be active. +##### +{COMMON_KEEP_A_B_C_D_UNIT_TEST "filter_adjacent_inactive" ( + [event] + name=start + + [object] + silent=yes + [effect] + apply_to=new_ability + [abilities] + [damage] + id=specialX + name=_ "specialX" + description=_ "specialX is active if and only if one of the adjacents units is Loki" + value=100 + apply_to=self + [filter_adjacent] + adjacent=n,ne,se,s,sw,nw + count=1-6 + id=Loki + [/filter_adjacent] + [/damage] + [/abilities] + [/effect] + [filter] + id=alice + [/filter] + [/object] + + {ASSERT ( + [not] + [have_unit] + ability_id_active=specialX + [/have_unit] + [/not] + )} + + {SUCCEED} + [/event] +)} diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_location_count_three_active.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_location_count_three_active.cfg new file mode 100644 index 00000000000..7e9a753fbd1 --- /dev/null +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_location_count_three_active.cfg @@ -0,0 +1,49 @@ +#textdomain wesnoth-test + +##### +# API(s) being tested: ability[filter_adjacent_location]adjacent,count= +## +# Actions: +# Give Alice an ability specialX, which is only active if exactly three of adjacents terrains are Gg. +# Test whether the ability is active. +## +# Expected end state: +# specialX should be active. +##### +{COMMON_KEEP_A_B_C_D_UNIT_TEST "filter_adjacent_location_count_three_active" ( + [event] + name=start + + [object] + silent=yes + [effect] + apply_to=new_ability + [abilities] + [damage] + id=specialX + name=_ "specialX" + description=_ "specialX is active if and only if 3 terrains hexes are Gg" + value=100 + apply_to=self + [filter_adjacent_location] + adjacent=n,ne,se,s,sw,nw + count=3 + terrain=Gg + [/filter_adjacent_location] + [/damage] + [/abilities] + [/effect] + [filter] + id=alice + [/filter] + [/object] + + {ASSERT ( + [have_unit] + ability_id_active=specialX + [/have_unit] + )} + + {SUCCEED} + [/event] +)} diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_location_count_zero_active.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_location_count_zero_active.cfg new file mode 100644 index 00000000000..3c8f392d1e5 --- /dev/null +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_location_count_zero_active.cfg @@ -0,0 +1,49 @@ +#textdomain wesnoth-test + +##### +# API(s) being tested: ability[filter_adjacent_location]adjacent,count= +## +# Actions: +# Give Alice an ability specialX, which is only active if zero of the adjacent terrains are Ww. +# Test whether the ability is active. +## +# Expected end state: +# specialX should be active. +##### +{COMMON_KEEP_A_B_C_D_UNIT_TEST "filter_adjacent_location_count_zero_active" ( + [event] + name=start + + [object] + silent=yes + [effect] + apply_to=new_ability + [abilities] + [damage] + id=specialX + name=_ "specialX" + description=_ "specialX is active if and only if 0 terrains hexes are Ww" + value=100 + apply_to=self + [filter_adjacent_location] + adjacent=n,ne,se,s,sw,nw + count=0 + terrain=Ww + [/filter_adjacent_location] + [/damage] + [/abilities] + [/effect] + [filter] + id=alice + [/filter] + [/object] + + {ASSERT ( + [have_unit] + ability_id_active=specialX + [/have_unit] + )} + + {SUCCEED} + [/event] +)} diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_location_count_zero_inactive.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_location_count_zero_inactive.cfg new file mode 100644 index 00000000000..6d5d1fbec74 --- /dev/null +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_adjacent_location_count_zero_inactive.cfg @@ -0,0 +1,49 @@ +##### +# API(s) being tested: ability[filter_adjacent_location]adjacent,count= +## +# Actions: +# Give Alice an ability specialX, which is only active if zero of the adjacent terrains are Gg. +# Test whether specialX ability is active. +## +# Expected end state: +# specialX isn't active. +##### +{COMMON_KEEP_A_B_C_D_UNIT_TEST "filter_adjacent_location_count_zero_inactive" ( + [event] + name=start + + [object] + silent=yes + [effect] + apply_to=new_ability + [abilities] + [damage] + id=specialX + name=_ "specialX" + description=_ "specialX is active if and only if 0 terrains hexes are Gg" + value=100 + apply_to=self + [filter_adjacent_location] + adjacent=n,ne,se,s,sw,nw + count=0 + terrain=Gg + [/filter_adjacent_location] + [/damage] + [/abilities] + [/effect] + [filter] + id=alice + [/filter] + [/object] + + {ASSERT ( + [not] + [have_unit] + ability_id_active=specialX + [/have_unit] + [/not] + )} + + {SUCCEED} + [/event] +)} diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_student_adjacent_active.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_student_adjacent_active.cfg new file mode 100644 index 00000000000..5b5b2e71109 --- /dev/null +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_student_adjacent_active.cfg @@ -0,0 +1,105 @@ +#textdomain wesnoth-test + +##### +# API(s) being tested: ability[filter_adjacent_student] +## +# Actions: +# place a unit alex adjacent to alice and only alice. +# Give alex an ability specialX who affect alice, which is only active in fight if one adjacent unit is bob. +# alice attack bob. +## +# Expected end state: +# attack event trigered if specialX active because alice is adjacent to bob. +##### +{GENERIC_UNIT_TEST "filter_adjacent_student_active" ( + [event] + name=start + + [unit] + id=alex + name=_"Alex" + x,y=12,4 + type=Elvish Hero + side=1 + [/unit] + [object] + silent=yes + [effect] + apply_to=new_ability + [abilities] + [damage] + id=specialX + name=_ "specialX" + description=_ "specialX is active if and only if one of the adjacents units is bob" + value=1 + apply_to=self + affect_self=no + affect_allies=yes + affect_enemies=yes + [affect_adjacent] + [filter] + id=alice + [/filter] + [/affect_adjacent] + [filter_adjacent_student] + id=bob + [/filter_adjacent_student] + [/damage] + [/abilities] + [/effect] + [filter] + id=alex + [/filter] + [/object] + {VARIABLE triggers_on_attack 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 when Alice attacks + [event] + name=attack + first_time_only=no + [filter_attack] + special_id_active=specialX + [/filter_attack] + {ASSERT ({VARIABLE_CONDITIONAL side_number equals 1})} + {VARIABLE_OP triggers_on_attack add 1} + [/event] + + [event] + name=turn 2 + {ASSERT ({VARIABLE_CONDITIONAL triggers_on_attack equals 1})} + {SUCCEED} + [/event] +)} diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_student_adjacent_inactive.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_student_adjacent_inactive.cfg new file mode 100644 index 00000000000..c65db61f962 --- /dev/null +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/filter_student_adjacent_inactive.cfg @@ -0,0 +1,113 @@ +#textdomain wesnoth-test + +##### +# API(s) being tested: ability[filter_adjacent_student] +## +# Actions: +# place a unit alex adjacent to alice and only alice. +# place loki adjacent to alex and only alex +# Give alex an ability specialX who affect alice, which is only active in fight if one adjacent unit is loki. +# alice attack bob. +## +# Expected end state: +# attack event no trigered if specialX active because alice is no adjacent to loki, [filter_adjacent_student] filter the units adjacent to student, and not owner of ability. +##### +{GENERIC_UNIT_TEST "filter_adjacent_student_inactive" ( + [event] + name=start + + [unit] + id=alex + name=_"Alex" + x,y=12,4 + type=Elvish Hero + side=1 + [/unit] + [unit] + id=loki + name=_"Loki" + x,y=11,4 + type=Elvish Hero + side=1 + [/unit] + [object] + silent=yes + [effect] + apply_to=new_ability + [abilities] + [damage] + id=specialX + name=_ "specialX" + description=_ "specialX is active if and only if one of the adjacents units is bob" + value=1 + apply_to=self + affect_self=no + affect_allies=yes + affect_enemies=yes + [affect_adjacent] + [filter] + id=alice + [/filter] + [/affect_adjacent] + [filter_adjacent_student] + id=loki + [/filter_adjacent_student] + [/damage] + [/abilities] + [/effect] + [filter] + id=alex + [/filter] + [/object] + {VARIABLE triggers_on_attack 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 when Alice attacks + [event] + name=attack + first_time_only=no + [filter_attack] + special_id_active=specialX + [/filter_attack] + {ASSERT ({VARIABLE_CONDITIONAL side_number equals 1})} + {VARIABLE_OP triggers_on_attack add 1} + [/event] + + [event] + name=turn 2 + {ASSERT ({VARIABLE_CONDITIONAL triggers_on_attack equals 0})} + {SUCCEED} + [/event] +)} diff --git a/src/units/abilities.cpp b/src/units/abilities.cpp index 80c41763418..62b1eabce11 100644 --- a/src/units/abilities.cpp +++ b/src/units/abilities.cpp @@ -446,18 +446,16 @@ bool unit::ability_active_impl(const std::string& ability,const config& cfg,cons std::size_t count = 0; unit_filter ufilt{ vconfig(i) }; ufilt.set_use_flat_tod(illuminates); - std::vector<map_location::direction> dirs = map_location::parse_directions(i["adjacent"]); + std::vector<map_location::direction> dirs = i["adjacent"].empty() ? map_location::all_directions() : map_location::parse_directions(i["adjacent"]); for (const map_location::direction index : dirs) { - if (index == map_location::direction::indeterminate) - continue; unit_map::const_iterator unit = units.find(adjacent[static_cast<int>(index)]); if (unit == units.end()) - return false; + continue; if (!ufilt(*unit, *this)) - return false; + continue; if((*this).id() == (*unit).id()) - return false; + continue; if (i.has_attribute("is_enemy")) { const display_context& dc = resources::filter_con->get_disp_context(); if (i["is_enemy"].to_bool() != dc.get_team(unit->side()).is_enemy(side_)) { @@ -466,10 +464,9 @@ bool unit::ability_active_impl(const std::string& ability,const config& cfg,cons } count++; } - if (i["count"].empty() && count != dirs.size()) { - return false; - } - if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) { + static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges_unsigned("1-6"); + config::attribute_value i_count =i["count"]; + if(!in_ranges<int>(count, !i_count.blank() ? utils::parse_ranges_unsigned(i_count) : default_counts)){ return false; } } @@ -480,21 +477,17 @@ bool unit::ability_active_impl(const std::string& ability,const config& cfg,cons terrain_filter adj_filter(vconfig(i), resources::filter_con, false); adj_filter.flatten(illuminates); - std::vector<map_location::direction> dirs = map_location::parse_directions(i["adjacent"]); + std::vector<map_location::direction> dirs = i["adjacent"].empty() ? map_location::all_directions() : map_location::parse_directions(i["adjacent"]); for (const map_location::direction index : dirs) { - if (index == map_location::direction::indeterminate) { - continue; - } if(!adj_filter.match(adjacent[static_cast<int>(index)])) { - return false; + continue; } count++; } - if (i["count"].empty() && count != dirs.size()) { - return false; - } - if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) { + static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges_unsigned("1-6"); + config::attribute_value i_count =i["count"]; + if(!in_ranges<int>(count, !i_count.blank() ? utils::parse_ranges_unsigned(i_count) : default_counts)){ return false; } } @@ -1554,13 +1547,13 @@ unit_ability_list attack_type::get_weapon_ability(const std::string& ability) co unit_ability_list abil_list(loc); if(self_) { abil_list.append_if((*self_).get_abilities(ability, self_loc_), [&](const unit_ability& i) { - return special_active(*i.ability_cfg, AFFECT_SELF, ability, "filter_student"); + return special_active(*i.ability_cfg, AFFECT_SELF, ability, true); }); } if(other_) { abil_list.append_if((*other_).get_abilities(ability, other_loc_), [&](const unit_ability& i) { - return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, "filter_student"); + return special_active_impl(other_attack_, shared_from_this(), *i.ability_cfg, AFFECT_OTHER, ability, true); }); } @@ -1757,7 +1750,7 @@ bool attack_type::check_self_abilities_impl(const const_attack_ptr& self_attack, } } if((*u).checking_tags().count(tag_name) != 0){ - if((*u).get_self_ability_bool(special, tag_name, loc) && special_active_impl(self_attack, other_attack, special, whom, tag_name, "filter_student")) { + if((*u).get_self_ability_bool(special, tag_name, loc) && special_active_impl(self_attack, other_attack, special, whom, tag_name, true)) { return true; } } @@ -1777,7 +1770,7 @@ bool attack_type::check_adj_abilities_impl(const const_attack_ptr& self_attack, } } if((*u).checking_tags().count(tag_name) != 0){ - if((*u).get_adj_ability_bool(special, tag_name, dir, loc, from) && special_active_impl(self_attack, other_attack, special, whom, tag_name, "filter_student")) { + if((*u).get_adj_ability_bool(special, tag_name, dir, loc, from) && special_active_impl(self_attack, other_attack, special, whom, tag_name, true)) { return true; } } @@ -2197,9 +2190,9 @@ bool attack_type::has_special_or_ability_with_filter(const config & filter) cons } bool attack_type::special_active(const config& special, AFFECTS whom, const std::string& tag_name, - const std::string& filter_self) const + bool in_abilities_tag) const { - return special_active_impl(shared_from_this(), other_attack_, special, whom, tag_name, filter_self); + return special_active_impl(shared_from_this(), other_attack_, special, whom, tag_name, in_abilities_tag); } /** @@ -2210,7 +2203,7 @@ bool attack_type::special_active(const config& special, AFFECTS whom, const std: * @param special a weapon special WML structure * @param whom specifies which combatant we care about * @param tag_name tag name of the special config - * @param filter_self the filter to use + * @param in_abilities_tag if special coded in [specials] or [abilities] tags */ bool attack_type::special_active_impl( const const_attack_ptr& self_attack, @@ -2218,7 +2211,7 @@ bool attack_type::special_active_impl( const config& special, AFFECTS whom, const std::string& tag_name, - const std::string& filter_self) + bool in_abilities_tag) { assert(self_attack || other_attack); bool is_attacker = self_attack ? self_attack->is_attacker_ : !other_attack->is_attacker_; @@ -2320,6 +2313,7 @@ bool attack_type::special_active_impl( //the function of this special in matches_filter() //In apply_to=both case, tag_name must be checked in all filter because special applied to both self and opponent. bool applied_both = special["apply_to"] == "both"; + const std::string& filter_self = in_abilities_tag ? "filter_student" : "filter_self"; std::string self_check_if_recursion = (applied_both || whom_is_self) ? tag_name : ""; if (!special_unit_matches(self, other, self_loc, self_attack, special, is_for_listing, filter_self, self_check_if_recursion)) return false; @@ -2337,21 +2331,25 @@ bool attack_type::special_active_impl( if (!special_unit_matches(def, att, def_loc, def_weapon, special, is_for_listing, "filter_defender", def_check_if_recursion)) return false; + //if filter_self != "filter_self" then it's in [abilities] tags and + //[filter_student_adjacent] and [filter_student_adjacent] then designate 'the student' (which may be different from the owner of the ability), + //while in the tags[specials] the usual names are kept. + const std::string& filter_adjacent = in_abilities_tag ? "filter_adjacent_student" : "filter_adjacent"; + const std::string& filter_adjacent_location = in_abilities_tag ? "filter_adjacent_student_location" : "filter_adjacent_location"; + const auto adjacent = get_adjacent_tiles(self_loc); // Filter the adjacent units. - for (const config &i : special.child_range("filter_adjacent")) + for (const config &i : special.child_range(filter_adjacent)) { std::size_t count = 0; - std::vector<map_location::direction> dirs = map_location::parse_directions(i["adjacent"]); + std::vector<map_location::direction> dirs = i["adjacent"].empty() ? map_location::all_directions() : map_location::parse_directions(i["adjacent"]); unit_filter filter{ vconfig(i) }; for (const map_location::direction index : dirs) { - if (index == map_location::direction::indeterminate) - continue; unit_map::const_iterator unit = units.find(adjacent[static_cast<int>(index)]); if (unit == units.end() || !filter.matches(*unit, adjacent[static_cast<int>(index)], *self)) - return false; + continue; if (i.has_attribute("is_enemy")) { const display_context& dc = resources::filter_con->get_disp_context(); if (i["is_enemy"].to_bool() != dc.get_team(unit->side()).is_enemy(self->side())) { @@ -2360,33 +2358,29 @@ bool attack_type::special_active_impl( } count++; } - if (i["count"].empty() && count != dirs.size()) { - return false; - } - if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) { + static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges_unsigned("1-6"); + config::attribute_value i_count =i["count"]; + if(!in_ranges<int>(count, !i_count.blank() ? utils::parse_ranges_unsigned(i_count) : default_counts)){ return false; } } // Filter the adjacent locations. - for (const config &i : special.child_range("filter_adjacent_location")) + for (const config &i : special.child_range(filter_adjacent_location)) { std::size_t count = 0; - std::vector<map_location::direction> dirs = map_location::parse_directions(i["adjacent"]); + std::vector<map_location::direction> dirs = i["adjacent"].empty() ? map_location::all_directions() : map_location::parse_directions(i["adjacent"]); terrain_filter adj_filter(vconfig(i), resources::filter_con, false); for (const map_location::direction index : dirs) { - if (index == map_location::direction::indeterminate) - continue; if(!adj_filter.match(adjacent[static_cast<int>(index)])) { - return false; + continue; } count++; } - if (i["count"].empty() && count != dirs.size()) { - return false; - } - if (!in_ranges<int>(count, utils::parse_ranges_unsigned(i["count"].str()))) { + static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges_unsigned("1-6"); + config::attribute_value i_count =i["count"]; + if(!in_ranges<int>(count, !i_count.blank() ? utils::parse_ranges_unsigned(i_count) : default_counts)){ return false; } } diff --git a/src/units/attack_type.hpp b/src/units/attack_type.hpp index a6c2c8ca1dd..cf80d0580a2 100644 --- a/src/units/attack_type.hpp +++ b/src/units/attack_type.hpp @@ -272,7 +272,7 @@ private: */ bool check_adj_abilities(const config& cfg, const std::string& special, int dir, const unit& from) const; bool special_active(const config& special, AFFECTS whom, const std::string& tag_name, - const std::string& filter_self ="filter_self") const; + bool in_abilities_tag = false) const; /** weapon_specials_impl_self and weapon_specials_impl_adj : check if special name can be added. * @param[in,out] temp_string the string modified and returned @@ -364,7 +364,7 @@ private: const config& special, AFFECTS whom, const std::string& tag_name, - const std::string& filter_self ="filter_self" + bool in_abilities_tag = false ); // Used via specials_context() to control which specials are diff --git a/wml_test_schedule b/wml_test_schedule index bd55a88f4ae..ec5555b79ec 100644 --- a/wml_test_schedule +++ b/wml_test_schedule @@ -411,6 +411,15 @@ 0 taught_resistance_with_three_attack_types 0 swarms_filter_student_by_type 0 swarms_effects_not_checkable +0 filter_adjacent_location_count_three_active +0 filter_adjacent_location_count_zero_active +0 filter_adjacent_location_count_zero_inactive +0 filter_adjacent_active +0 filter_adjacent_inactive +0 filter_adjacent_student_active +0 filter_adjacent_student_inactive +0 filter_adjacent_direction_active +0 filter_adjacent_direction_inactive 0 filter_special_id_active 0 filter_ability_special_id_active 0 filter_special_id_not_exists