WML: Add separate special_id and special_type checks to weapon filtering
PR #4424, fixes #3915.
This commit is contained in:
parent
0f09fce0e1
commit
a87be48307
5 changed files with 219 additions and 19 deletions
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue