diff --git a/changelog_entries/[damage]_special_changelog.md b/changelog_entries/[damage]_special_changelog.md index ddfa06e3f473b..a57a5c7cba1bd 100644 --- a/changelog_entries/[damage]_special_changelog.md +++ b/changelog_entries/[damage]_special_changelog.md @@ -1,2 +1,2 @@ ### WML Engine - * Add a 'replacement_type' and 'alternative_type' attribute to [damage] to change the type of attack under specific conditions (terrain, time of day, leadership etc...) \ No newline at end of file + * Add a 'neg_alternative__type' and 'alternative_type' attribute to [damage] to change the type of attack under specific conditions (terrain, time of day, leadership etc...) \ No newline at end of file diff --git a/data/schema/filters/abilities.cfg b/data/schema/filters/abilities.cfg index b0c94a7ab8e51..85cc2258d5dac 100644 --- a/data/schema/filters/abilities.cfg +++ b/data/schema/filters/abilities.cfg @@ -7,7 +7,7 @@ {SIMPLE_KEY overwrite_specials ability_overwrite} {SIMPLE_KEY apply_to string_list} {SIMPLE_KEY type string_list} - {SIMPLE_KEY replacement_type string_list} + {SIMPLE_KEY neg_alternative_type string_list} {SIMPLE_KEY alternative_type string_list} {SIMPLE_KEY active_on ability_context} {SIMPLE_KEY value s_int_range_list} diff --git a/data/schema/filters/weapon.cfg b/data/schema/filters/weapon.cfg index 1de2ff621c9f3..5084885a061a3 100644 --- a/data/schema/filters/weapon.cfg +++ b/data/schema/filters/weapon.cfg @@ -21,11 +21,3 @@ {FILTER_BOOLEAN_OPS weapon} [/tag] -[tag] - name="$filter_weapon_by_special" - max=0 - super="$filter_weapon" - {SIMPLE_KEY modified_type string_list} - {FILTER_BOOLEAN_OPS weapon} -[/tag] - diff --git a/data/schema/units/abilities.cfg b/data/schema/units/abilities.cfg index 5e12131b7bf5d..7b2c82862f708 100644 --- a/data/schema/units/abilities.cfg +++ b/data/schema/units/abilities.cfg @@ -83,8 +83,8 @@ name="resistance" max=infinite super="units/unit_type/abilities/~value~" - {FILTER_TAG "filter_weapon" weapon_by_special ()} - {FILTER_TAG "filter_second_weapon" weapon_by_special ()} + {FILTER_TAG "filter_weapon" weapon ()} + {FILTER_TAG "filter_second_weapon" weapon ()} [/tag] [tag] name="leadership" @@ -95,8 +95,8 @@ {SIMPLE_KEY sub f_int} {SIMPLE_KEY multiply f_int} {SIMPLE_KEY divide f_int} - {FILTER_TAG "filter_weapon" weapon_by_special ()} - {FILTER_TAG "filter_second_weapon" weapon_by_special ()} + {FILTER_TAG "filter_weapon" weapon ()} + {FILTER_TAG "filter_second_weapon" weapon ()} [/tag] [tag] name="illuminates" diff --git a/data/schema/units/specials.cfg b/data/schema/units/specials.cfg index ad6e2cd480a1d..799b067616be1 100644 --- a/data/schema/units/specials.cfg +++ b/data/schema/units/specials.cfg @@ -14,10 +14,10 @@ {FILTER_TAG "filter_adjacent" adjacent ()} {FILTER_TAG "filter_adjacent_location" adjacent_location ()} - {FILTER_TAG "filter_self" unit {FILTER_TAG "filter_weapon" weapon_by_special ()}} - {FILTER_TAG "filter_opponent" unit {FILTER_TAG "filter_weapon" weapon_by_special ()}} - {FILTER_TAG "filter_attacker" unit {FILTER_TAG "filter_weapon" weapon_by_special ()}} - {FILTER_TAG "filter_defender" unit {FILTER_TAG "filter_weapon" weapon_by_special ()}} + {FILTER_TAG "filter_self" unit {FILTER_TAG "filter_weapon" weapon ()}} + {FILTER_TAG "filter_opponent" unit {FILTER_TAG "filter_weapon" weapon ()}} + {FILTER_TAG "filter_attacker" unit {FILTER_TAG "filter_weapon" weapon ()}} + {FILTER_TAG "filter_defender" unit {FILTER_TAG "filter_weapon" weapon ()}} {WML_MERGE_KEYS} [/tag] # A few specials inheriting from ~generic~ are included here so that unit abilities can then inherit from them. diff --git a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/special_damage_type.cfg b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/special_damage_type.cfg index a47382c021c3e..f13f6d254e26b 100644 --- a/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/special_damage_type.cfg +++ b/data/test/scenarios/wml_tests/UnitsWML/AbilitiesWML/special_damage_type.cfg @@ -25,8 +25,8 @@ apply_to=resistance replace=yes [resistance] - arcane=50 fire=200 + blade=100 [/resistance] [/effect] [effect] @@ -40,15 +40,15 @@ value=12 [/damage] [damage] - replacement_type=fire + neg_alternative_type=fire {FILTER_WEAPON} [/damage] [damage] - replacement_type=cold + neg_alternative_type=cold {FILTER_WEAPON} [/damage] [damage] - replacement_type=cold + neg_alternative_type=cold {FILTER_WEAPON} [/damage] [chance_to_hit] @@ -66,8 +66,8 @@ apply_to=resistance replace=yes [resistance] - cold=200 - fire=50 + cold=50 + fire=200 [/resistance] [/effect] [effect] @@ -81,22 +81,22 @@ value=12 [/damage] [damage] - replacement_type=cold + neg_alternative_type=cold [/damage] [damage] - replacement_type=arcane + neg_alternative_type=arcane [/damage] [damage] - replacement_type=arcane + neg_alternative_type=arcane [/damage] [damage] - replacement_type=fire + neg_alternative_type=fire [/damage] [damage] - replacement_type=fire + neg_alternative_type=fire [/damage] [damage] - replacement_type=fire + neg_alternative_type=fire [/damage] [chance_to_hit] value=100 @@ -157,42 +157,41 @@ [/filter] variable=b [/store_unit] - #damage without modification are 12, if test fail hitpoints !=76 - #if succed then damage by alice 24(bob vulnerable to fire and fire prioritized) - #if succed then damage by bob 24(alice vulnerable to cold, cold priority is 1) - {ASSERT ({VARIABLE_CONDITIONAL a.hitpoints equals 76})} - {ASSERT ({VARIABLE_CONDITIONAL b.hitpoints equals 76})} + #if succed then damage by alice 12(bob resistant to fire and fire prioritized) + #if succed then damage by bob 6(alice resistant to cold) + {ASSERT ({VARIABLE_CONDITIONAL a.hitpoints equals 94})} + {ASSERT ({VARIABLE_CONDITIONAL b.hitpoints equals 88})} {SUCCEED} [/event] #enddef ##### -# API(s) being tested: [damage]replacement_type= +# API(s) being tested: [damage]neg_alternative_type= ## # Actions: # Give both Alice and Bob 100% chance to hit. -# Give Bob one [damage] with replacement_type=fire and two [damage] with replacement_type=cold -# change resistance of Bob to arcane to 50% and fire to -100%. -# Give Alice one [damage] with replacement_type=cold, two [damage] with replacement_type=arcane and three with replacement_type=fire -# and change Alice resistance to cold to -100% and fire to 50%. +# Give Bob one [damage] with neg_alternative_type=fire and two [damage] with neg_alternative_type=cold +# change resistance of Bob to fire to -100%. +# Give Alice one [damage] with neg_alternative_type=cold, two [damage] with neg_alternative_type=arcane and three with neg_alternative_type=fire +# and change Alice resistance to cold to -50% and fire to -100%. # Move Alice next to Bob, and have Alice attack Bob. ## # Expected end state: -# Alice attack with fire and Bob use cold. +# Alice attack with blade and Bob use cold. ##### {GENERIC_UNIT_TEST "damage_type_test" ( {DAMAGE_TYPE_TEST ()} )} ##### -# API(s) being tested: [damage]replacement_type=[filter_self][filter_weapon]type=blade +# API(s) being tested: [damage]neg_alternative_type=[filter_self][filter_weapon]type=blade ## # Actions: # Give both Alice and Bob 100% chance to hit. -# Give Bob one [damage] with replacement_type=fire and two [damage] with replacement_type=cold and filter by type=blade -# change resistance of Bob to arcane to 50% and fire to -100%. -# Give Alice one [damage] with replacement_type=cold, two [damage] with replacement_type=arcane and three with replacement_type=fire -# and change Alice resistance to cold to -100% and fire to 50%. +# Give Bob one [damage] with neg_alternative_type=fire and two [damage] with neg_alternative_type=cold and filter [damage] with type=blade +# change resistance of Bob to fire to -100%. +# Give Alice one [damage] with neg_alternative_type=cold, two [damage] with neg_alternative_type=arcane and three with neg_alternative_type=fire +# and change Alice resistance to cold to -50% and fire to -100%. # Move Alice next to Bob, and have Alice attack Bob. ## # Expected end state: diff --git a/src/gui/dialogs/attack_predictions.cpp b/src/gui/dialogs/attack_predictions.cpp index 1eec8db2f718c..44b3952f16542 100644 --- a/src/gui/dialogs/attack_predictions.cpp +++ b/src/gui/dialogs/attack_predictions.cpp @@ -204,11 +204,15 @@ void attack_predictions::set_data(window& window, const combatant_data& attacker } std::pair types = weapon->damage_type(); - std::string type_bis = types.second; - if (!type_bis.empty()) { - type_bis = ", " + string_table["type_" + type_bis]; + std::string neg_alt_type = types.first; + std::string pos_alt_type = types.second; + if (!neg_alt_type.empty()) { + neg_alt_type = ", " + font::span_color(font::bad_dmg_color, string_table["type_" + neg_alt_type]); } - ss << string_table["type_" + types.first] + type_bis; + if (!pos_alt_type.empty()) { + pos_alt_type = ", " + font::span_color(font::good_dmg_color, string_table["type_" + pos_alt_type]); + } + ss << string_table["type_" + weapon->type()] + neg_alt_type + pos_alt_type; set_label_helper("resis_label", ss.str()); diff --git a/src/gui/dialogs/unit_attack.cpp b/src/gui/dialogs/unit_attack.cpp index 8d008a683e3a0..270a93d105ff9 100644 --- a/src/gui/dialogs/unit_attack.cpp +++ b/src/gui/dialogs/unit_attack.cpp @@ -116,22 +116,30 @@ void unit_attack::pre_show(window& window) ); std::pair types = attacker_weapon.damage_type(); - std::string attw_type_second = types.second; - std::string attw_type = !(types.first).empty() ? types.first : attacker_weapon.type(); + std::string neg_alt_attw_type = types.first; + std::string pos_alt_attw_type = types.second; + std::string attw_type = attacker_weapon.type(); if (!attw_type.empty()) { attw_type = string_table["type_" + attw_type]; } - if (!attw_type_second.empty()) { - attw_type_second = ", " + string_table["type_" + attw_type_second]; + if (!neg_alt_attw_type.empty()) { + neg_alt_attw_type = ", " + font::span_color(font::bad_dmg_color, string_table["type_" + neg_alt_attw_type]); + } + if (!pos_alt_attw_type.empty()) { + pos_alt_attw_type = ", " + font::span_color(font::good_dmg_color, string_table["type_" + pos_alt_attw_type]); } std::pair def_types = defender_weapon.damage_type(); - std::string defw_type_second = def_types.second; - std::string defw_type = !(def_types.first).empty() ? def_types.first : defender_weapon.type(); + std::string neg_alt_defw_type = def_types.first; + std::string pos_alt_defw_type = types.second; + std::string defw_type = defender_weapon.type(); if (!defw_type.empty()) { defw_type = string_table["type_" + defw_type]; } - if (!defw_type_second.empty()) { - defw_type_second = ", " + string_table["type_" + defw_type_second]; + if (!neg_alt_defw_type.empty()) { + neg_alt_defw_type = ", " + font::span_color(font::bad_dmg_color, string_table["type_" + neg_alt_defw_type]); + } + if (!pos_alt_defw_type.empty()) { + pos_alt_defw_type = ", " + font::span_color(font::good_dmg_color, string_table["type_" + pos_alt_defw_type]); } const std::set checking_tags_other = {"disable", "berserk", "drains", "heal_on_hit", "plague", "slow", "petrifies", "firststrike", "poison"}; @@ -182,26 +190,26 @@ void unit_attack::pre_show(window& window) // Use attacker/defender.num_blows instead of attacker/defender_weapon.num_attacks() because the latter does not consider the swarm weapon special attacker_stats << "" << attw_name << "" << "\n" - << attw_type << attw_type_second << "\n" + << attw_type << neg_alt_attw_type << pos_alt_attw_type << "\n" << attacker.damage << font::weapon_numbers_sep << attacker.num_blows << attw_specials << "\n" << font::span_color(a_cth_color) << attacker.chance_to_hit << "%"; attacker_tooltip << _("Weapon: ") << "" << attw_name << "" << "\n" - << _("Type: ") << attw_type << attw_type_second << "\n" + << _("Type: ") << attw_type << neg_alt_attw_type << pos_alt_attw_type << "\n" << _("Damage: ") << attacker.damage << "" << attw_specials_dmg << "" << "\n" << _("Attacks: ") << attacker.num_blows << "" << attw_specials_atk << "" << "\n" << _("Chance to hit: ") << font::span_color(a_cth_color) << attacker.chance_to_hit << "%"<< "" << attw_specials_cth << "" << attw_specials_others; defender_stats << "" << defw_name << "" << "\n" - << defw_type << defw_type_second << "\n" + << defw_type << neg_alt_defw_type << pos_alt_defw_type << "\n" << defender.damage << font::weapon_numbers_sep << defender.num_blows << defw_specials << "\n" << font::span_color(d_cth_color) << defender.chance_to_hit << "%"; defender_tooltip << _("Weapon: ") << "" << defw_name << "" << "\n" - << _("Type: ") << defw_type << defw_type_second << "\n" + << _("Type: ") << defw_type << neg_alt_defw_type << pos_alt_defw_type << "\n" << _("Damage: ") << defender.damage << "" << defw_specials_dmg << "" << "\n" << _("Attacks: ") << defender.num_blows << "" << defw_specials_atk << "" << "\n" << _("Chance to hit: ") << font::span_color(d_cth_color) << defender.chance_to_hit << "%"<< "" << defw_specials_cth << "" diff --git a/src/movetype.cpp b/src/movetype.cpp index a86b946ba596e..817bb65699bff 100644 --- a/src/movetype.cpp +++ b/src/movetype.cpp @@ -744,7 +744,10 @@ utils::string_map_res movetype::resistances::damage_table() const int movetype::resistances::resistance_against(const attack_type & attack) const { std::pair types = attack.damage_type(); - int res = resistance_against(types.first); + int res = resistance_against(attack.type()); + if(!(types.first).empty()){ + res = std::min(res, resistance_against(types.first)); + } if(!(types.second).empty()){ res = std::max(res, resistance_against(types.second)); } diff --git a/src/reports.cpp b/src/reports.cpp index c520043cf245f..e2e429fbd075d 100644 --- a/src/reports.cpp +++ b/src/reports.cpp @@ -877,21 +877,27 @@ static int attack_info(const reports::context& rc, const attack_type &at, config std::string range = string_table["range_" + at.range()]; std::pair types = at.damage_type(); - std::string secondary_lang_type = types.second; - if (!secondary_lang_type.empty()) { - secondary_lang_type = ", " + string_table["type_" + secondary_lang_type]; + std::string neg_alt_lang_type = types.first; + if (!neg_alt_lang_type.empty()) { + neg_alt_lang_type = ", " + font::span_color(font::bad_dmg_color, string_table["type_" + neg_alt_lang_type]); } - std::string lang_type = string_table["type_" + types.first] + secondary_lang_type; + std::string pos_alt_lang_type = types.second; + if (!pos_alt_lang_type.empty()) { + pos_alt_lang_type = ", " + font::span_color(font::good_dmg_color, string_table["type_" + pos_alt_lang_type]); + } + std::string lang_type = string_table["type_" + at.type()] + neg_alt_lang_type + pos_alt_lang_type; // SCALE_INTO() is needed in case the 72x72 images/misc/missing-image.png is substituted. const std::string range_png = std::string("icons/profiles/") + at.range() + "_attack.png~SCALE_INTO(16,16)"; - const std::string type_png = std::string("icons/profiles/") + types.first + ".png~SCALE_INTO(16,16)"; - const std::string secondary_type_png = !(types.second).empty() ? std::string("icons/profiles/") + types.second + ".png~SCALE_INTO(16,16)" : ""; + const std::string type_png = std::string("icons/profiles/") + at.type() + ".png~SCALE_INTO(16,16)"; + const std::string neg_type_png = !(types.first).empty() ? std::string("icons/profiles/") + types.first + ".png~SCALE_INTO(16,16)" : ""; + const std::string pos_type_png = !(types.second).empty() ? std::string("icons/profiles/") + types.second + ".png~SCALE_INTO(16,16)" : ""; const bool range_png_exists = image::locator(range_png).file_exists(); const bool type_png_exists = image::locator(type_png).file_exists(); - const bool secondary_type_png_exists = image::locator(secondary_type_png).file_exists(); + const bool neg_type_png_exists = image::locator(neg_type_png).file_exists(); + const bool pos_type_png_exists = image::locator(pos_type_png).file_exists(); - if(!range_png_exists || !type_png_exists || (!secondary_type_png_exists && !secondary_lang_type.empty())) { + if(!range_png_exists || !type_png_exists || (!neg_type_png_exists && !neg_alt_lang_type.empty()) || (!pos_type_png_exists && !pos_alt_lang_type.empty())) { str << span_color(font::weapon_details_color) << " " << " " << range << font::weapon_details_sep << lang_type << "\n"; @@ -948,8 +954,12 @@ static int attack_info(const reports::context& rc, const attack_type &at, config const std::string spacer = "misc/blank.png~CROP(0, 0, 16, 21)"; // 21 == 16+5 add_image(res, spacer + "~BLIT(" + range_png + ",0,5)", damage_versus.tooltip); add_image(res, spacer + "~BLIT(" + type_png + ",0,5)", damage_versus.tooltip); - if(secondary_type_png_exists){ - add_image(res, spacer + "~BLIT(" + secondary_type_png + ",0,5)", damage_versus.tooltip); + add_image(res, spacer + "~BLIT(" + type_png + ",0,5)", damage_versus.tooltip); + if(neg_type_png_exists){ + add_image(res, spacer + "~BLIT(" + neg_type_png + ",0,5)", damage_versus.tooltip); + } + if(pos_type_png_exists){ + add_image(res, spacer + "~BLIT(" + pos_type_png + ",0,5)", damage_versus.tooltip); } add_text(res, damage_and_num_attacks.str, damage_and_num_attacks.tooltip); add_text(res, damage_versus.str, damage_versus.tooltip); // This string is usually empty diff --git a/src/units/abilities.cpp b/src/units/abilities.cpp index 5ae9e2ef22f28..dc15c6588c97d 100644 --- a/src/units/abilities.cpp +++ b/src/units/abilities.cpp @@ -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 @@ -1199,15 +1199,15 @@ std::pair attack_type::damage_type() const { unit_ability_list abil_list = get_specials_and_abilities("damage"); if(abil_list.empty()){ - return {type(), ""}; + return {"", ""}; } - std::vector type_list = damage_type_list(abil_list, "replacement_type"); + std::vector type_list = damage_type_list(abil_list, "neg_alternative_type"); std::vector added_type_list = damage_type_list(abil_list, "alternative_type"); - std::string type_damage, sec_type_damage; - type_damage = !type_list.empty() ? type_list.front() : type(); - sec_type_damage = !added_type_list.empty() ? added_type_list.front() : ""; - return {type_damage, sec_type_damage}; + std::string neg_type_damage, pos_type_damage; + neg_type_damage = !type_list.empty() ? type_list.front() : ""; + pos_type_damage = !added_type_list.empty() ? added_type_list.front() : ""; + return {neg_type_damage, pos_type_damage}; } @@ -1312,7 +1312,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) ) return false; } diff --git a/src/units/attack_type.cpp b/src/units/attack_type.cpp index f3bc0aabdf9cd..1058ad281d5ab 100644 --- a/src/units/attack_type.cpp +++ b/src/units/attack_type.cpp @@ -97,11 +97,16 @@ std::string attack_type::accuracy_parry_description() const return s.str(); } + +static bool check_string(const std::vector& 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 can_loop) +static bool matches_simple_filter(const attack_type & attack, const config & filter) { const std::vector& filter_range = utils::split(filter["range"]); const std::string& filter_damage = filter["damage"]; @@ -145,25 +150,9 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil return false; if (!filter_type.empty()){ - if(can_loop){ - if (std::find(filter_type.begin(), filter_type.end(), attack.type()) == filter_type.end() ){ + std::pair damage_type = attack.damage_type(); + if (!check_string(filter_type, attack.type()) && !check_string(filter_type, damage_type.first) && !check_string(filter_type, damage_type.second)){ return false; - } - } else { - std::pair 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())){ - return false; - } - } - } - - if(can_loop){ - const std::vector filter_modified_type = utils::split(filter["modified_type"]); - if (!filter_modified_type.empty()){ - std::pair damage_type = attack.damage_type(); - if ((std::find(filter_modified_type.begin(), filter_modified_type.end(), damage_type.first) == filter_modified_type.end()) && (std::find(filter_modified_type.begin(), filter_modified_type.end(), damage_type.second) == filter_modified_type.end())){ - return false; - } } } @@ -265,25 +254,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 can_loop) const +bool attack_type::matches_filter(const config& filter) const { // Handle the basic filter. - bool matches = matches_simple_filter(*this, filter, can_loop); + bool matches = matches_simple_filter(*this, 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 && matches_filter(condition.cfg, can_loop); + matches = matches && matches_filter(condition.cfg); // Handle [or] else if ( condition.key == "or" ) - matches = matches || matches_filter(condition.cfg, can_loop); + matches = matches || matches_filter(condition.cfg); // Handle [not] else if ( condition.key == "not" ) - matches = matches && !matches_filter(condition.cfg, can_loop); + matches = matches && !matches_filter(condition.cfg); } return matches; diff --git a/src/units/attack_type.hpp b/src/units/attack_type.hpp index 6be9874f6f610..6073573e04868 100644 --- a/src/units/attack_type.hpp +++ b/src/units/attack_type.hpp @@ -124,7 +124,7 @@ class attack_type : public std::enable_shared_from_this // In unit_types.cpp: - bool matches_filter(const config& filter, bool can_loop = false) const; + bool matches_filter(const config& filter) const; bool apply_modification(const config& cfg); bool describe_modification(const config& cfg,std::string* description); diff --git a/src/units/unit.cpp b/src/units/unit.cpp index d37dc6c4220a7..9cf8af583e8dd 100644 --- a/src/units/unit.cpp +++ b/src/units/unit.cpp @@ -1461,7 +1461,7 @@ static bool matches_ability_filter(const config & cfg, const std::string& tag_na return false; //for damage only - if(!string_matches_if_present(filter, cfg, "replacement_type", "")) + if(!string_matches_if_present(filter, cfg, "neg_alternative_type", "")) return false; if(!string_matches_if_present(filter, cfg, "alternative_type", ""))