WML: Add separate special_id and special_type checks to weapon filtering

PR #4424, fixes #3915.
This commit is contained in:
newfrenchy83 2019-10-16 02:55:13 +00:00 committed by josteph
parent 0f09fce0e1
commit a87be48307
5 changed files with 219 additions and 19 deletions

View file

@ -183,7 +183,7 @@ Xu, Xu, Qxu, Qxu, Ql, Ql, Ql, Xu, Xu, Xu, Xu, Xu, Xu, Xu, Xu, Gg, Gg, Gg, Gg, Gg
[specials]
{WEAPON_SPECIAL_MARKSMAN}
[drains]
id=drains
id=drains20
name="drains 20%"
description="This unit drains health from living units, healing itself for 20% of the amount of damage it deals (rounded down)."
value=20
@ -467,7 +467,7 @@ Xu, Xu, Qxu, Qxu, Ql, Ql, Ql, Xu, Xu, Xu, Xu, Xu, Xu, Xu, Xu, Gg, Gg, Gg, Gg, Gg
[specials]
{WEAPON_SPECIAL_MARKSMAN}
[drains]
id=drains
id=drains20
name="drains 20%"
description="This unit drains health from living units, healing itself for 20% of the amount of damage it deals (rounded down)."
value=20
@ -532,6 +532,84 @@ Xu, Xu, Qxu, Qxu, Ql, Ql, Ql, Xu, Xu, Xu, Xu, Xu, Xu, Xu, Xu, Gg, Gg, Gg, Gg, Gg
vision=15
[/modify_unit]
[/event]
[event]
name=attack_end
first_time_only=yes
[filter_attack]
special=chance_to_hit
[/filter_attack]
[message]
speaker=unit
message="I used a chance_to_hit attack special."
[/message]
[/event]
[event]
name=attack_end
first_time_only=yes
[filter_attack]
special=magical
[/filter_attack]
[message]
speaker=unit
message="I used a magical attack."
[/message]
[/event]
[event]
name=attack_end
first_time_only=yes
[filter_attack]
special_id=magical
[/filter_attack]
[message]
speaker=unit
message="I used a magical attack."
[/message]
[/event]
[event]
name=attack_end
first_time_only=yes
[filter_attack]
special_id=chance_to_hit
[/filter_attack]
[message]
speaker=unit
message="BUG: This message should never be displayed!"
[/message]
[/event]
[event]
name=attack_end
first_time_only=yes
[filter_attack]
special_type=chance_to_hit
[/filter_attack]
[message]
speaker=unit
message="I used a chance_to_hit type weapon special."
[/message]
[/event]
[event]
name=attack_end
first_time_only=yes
[filter_attack]
special_type=magical
[/filter_attack]
[message]
speaker=unit
message="BUG: This message should never be displayed!"
[/message]
[/event]
[label]
x,y=18,7

View file

@ -7,6 +7,10 @@
{SIMPLE_KEY type string_list}
{SIMPLE_KEY special string_list}
{SIMPLE_KEY special_active string_list}
{SIMPLE_KEY special_id string_list}
{SIMPLE_KEY special_id_active string_list}
{SIMPLE_KEY special_type string_list}
{SIMPLE_KEY special_type_active string_list}
{SIMPLE_KEY formula formula}
{SIMPLE_KEY damage s_range_list}
{SIMPLE_KEY number s_range_list}

View file

@ -628,6 +628,40 @@ namespace {
}
return false;
}
bool get_special_children_id(std::vector<special_match>& id_result,
const config& parent, const std::string& id,
bool just_peeking=false) {
for (const config::any_child &sp : parent.all_children_range())
{
if (just_peeking && (sp.cfg["id"] == id)) {
return true; // peek succeeded; done
}
if(sp.cfg["id"] == id) {
special_match special = { sp.key, &sp.cfg };
id_result.push_back(special);
}
}
return false;
}
bool get_special_children_tags(std::vector<special_match>& tag_result,
const config& parent, const std::string& id,
bool just_peeking=false) {
for (const config::any_child &sp : parent.all_children_range())
{
if (just_peeking && (sp.key == id)) {
return true; // peek succeeded; done
}
if(sp.key == id) {
special_match special = { sp.key, &sp.cfg };
tag_result.push_back(special);
}
}
return false;
}
}
/**
@ -637,25 +671,39 @@ namespace {
* active in the current context (see set_specials_context), including
* specials obtained from the opponent's attack.
*/
bool attack_type::get_special_bool(const std::string& special, bool simple_check) const
bool attack_type::get_special_bool(const std::string& special, bool simple_check, bool special_id, bool special_tags) const
{
{
std::vector<special_match> special_tag_matches;
std::vector<special_match> special_id_matches;
if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
return true;
if(special_id && special_tags){
if ( get_special_children(special_tag_matches, special_id_matches, specials_, special, simple_check) ) {
return true;
}
} else if(special_id && !special_tags){
if ( get_special_children_id(special_id_matches, specials_, special, simple_check) ) {
return true;
}
} else if(!special_id && special_tags){
if ( get_special_children_tags(special_tag_matches, specials_, special, simple_check) ) {
return true;
}
}
// If we make it to here, then either list.empty() or !simple_check.
// So if the list is not empty, then this is not a simple check and
// we need to check each special in the list to see if any are active.
for(const special_match& entry : special_tag_matches) {
if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
return true;
if(special_tags){
for(const special_match& entry : special_tag_matches) {
if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
return true;
}
}
}
for(const special_match& entry : special_id_matches) {
if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
return true;
if(special_id){
for(const special_match& entry : special_id_matches) {
if ( special_active(*entry.cfg, AFFECT_SELF, entry.tag_name) ) {
return true;
}
}
}
}
@ -667,15 +715,25 @@ bool attack_type::get_special_bool(const std::string& special, bool simple_check
std::vector<special_match> special_tag_matches;
std::vector<special_match> special_id_matches;
get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
for(const special_match& entry : special_tag_matches) {
if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
return true;
if(special_id && special_tags){
get_special_children(special_tag_matches, special_id_matches, other_attack_->specials_, special);
} else if(special_id && !special_tags){
get_special_children_id(special_id_matches, other_attack_->specials_, special);
} else if(!special_id && special_tags){
get_special_children_tags(special_tag_matches, other_attack_->specials_, special);
}
if(special_tags){
for(const special_match& entry : special_tag_matches) {
if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
return true;
}
}
}
for(const special_match& entry : special_id_matches) {
if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
return true;
if(special_id){
for(const special_match& entry : special_id_matches) {
if ( other_attack_->special_active(*entry.cfg, AFFECT_OTHER, entry.tag_name) ) {
return true;
}
}
}
return false;

View file

@ -22,6 +22,8 @@
#include "formula/formula.hpp"
#include "formula/string_utils.hpp"
#include "formula/function_gamestate.hpp"
#include "deprecation.hpp"
#include "game_version.hpp"
#include "lexical_cast.hpp"
#include "log.hpp"
@ -105,7 +107,11 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
const std::vector<std::string> filter_name = utils::split(filter["name"]);
const std::vector<std::string> filter_type = utils::split(filter["type"]);
const std::vector<std::string> filter_special = utils::split(filter["special"]);
const std::vector<std::string> filter_special_id = utils::split(filter["special_id"]);
const std::vector<std::string> filter_special_type = utils::split(filter["special_type"]);
const std::vector<std::string> filter_special_active = utils::split(filter["special_active"]);
const std::vector<std::string> filter_special_id_active = utils::split(filter["special_id_active"]);
const std::vector<std::string> filter_special_type_active = utils::split(filter["special_type_active"]);
const std::string filter_formula = filter["formula"];
if ( !filter_range.empty() && std::find(filter_range.begin(), filter_range.end(), attack.range()) == filter_range.end() )
@ -133,6 +139,7 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
return false;
if(!filter_special.empty()) {
deprecated_message("special=", DEP_LEVEL::PREEMPTIVE, {1, 17, 0}, "Please use special_id or special_type instead");
bool found = false;
for(auto& special : filter_special) {
if(attack.get_special_bool(special, true)) {
@ -144,8 +151,21 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
return false;
}
}
if(!filter_special_id.empty()) {
bool found = false;
for(auto& special : filter_special_id) {
if(attack.get_special_bool(special, true, true, false)) {
found = true;
break;
}
}
if(!found) {
return false;
}
}
if(!filter_special_active.empty()) {
deprecated_message("special_active=", DEP_LEVEL::PREEMPTIVE, {1, 17, 0}, "Please use special_id_active or special_type_active instead");
bool found = false;
for(auto& special : filter_special_active) {
if(attack.get_special_bool(special, false)) {
@ -157,6 +177,42 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
return false;
}
}
if(!filter_special_id_active.empty()) {
bool found = false;
for(auto& special : filter_special_id_active) {
if(attack.get_special_bool(special, false, true, false)) {
found = true;
break;
}
}
if(!found) {
return false;
}
}
if(!filter_special_type.empty()) {
bool found = false;
for(auto& special : filter_special_type) {
if(attack.get_special_bool(special, true, false)) {
found = true;
break;
}
}
if(!found) {
return false;
}
}
if(!filter_special_type_active.empty()) {
bool found = false;
for(auto& special : filter_special_type_active) {
if(attack.get_special_bool(special, false, false)) {
found = true;
break;
}
}
if(!found) {
return false;
}
}
if (!filter_formula.empty()) {
try {

View file

@ -73,7 +73,11 @@ public:
// In unit_abilities.cpp:
bool get_special_bool(const std::string& special, bool simple_check=false) const;
/// @return True iff the special @a special is active.
/// @param simple_check If true, check whether the unit has the special. Else, check whether the special is currently active.
/// @param special_id If true, match @a special against the @c id of special tags.
/// @param special_tags If true, match @a special against the tag name of special tags.
bool get_special_bool(const std::string& special, bool simple_check=false, bool special_id=true, bool special_tags=true) const;
unit_ability_list get_specials(const std::string& special) const;
std::vector<std::pair<t_string, t_string>> special_tooltips(boost::dynamic_bitset<>* active_list = nullptr) const;
std::string weapon_specials(bool only_active=false, bool is_backstab=false) const;