move unit_filter implementation out of unit

The purpose is to simplify the unit class, and perhaps allow for
some optimizations regarding how unit filters are impemented.
This commit is contained in:
Chris Beck 2014-07-03 03:47:24 -04:00
parent 4046666bee
commit d803cde06a
22 changed files with 578 additions and 460 deletions

View file

@ -1039,6 +1039,8 @@
<Unit filename="..\..\src\unit_display.hpp" />
<Unit filename="..\..\src\unit_drawer.cpp" />
<Unit filename="..\..\src\unit_drawer.hpp" />
<Unit filename="..\..\src\unit_filter.cpp" />
<Unit filename="..\..\src\unit_filter.hpp" />
<Unit filename="..\..\src\unit_formula_manager.cpp" />
<Unit filename="..\..\src\unit_formula_manager.hpp" />
<Unit filename="..\..\src\unit_frame.cpp" />

View file

@ -21192,6 +21192,14 @@
RelativePath="..\..\src\unit_drawer.hpp"
>
</File>
<File
RelativePath="..\..\src\unit_filter.cpp"
>
</File>
<File
RelativePath="..\..\src\unit_filter.hpp"
>
</File>
<File
RelativePath="..\..\src\unit_formula_manager.cpp"
>

View file

@ -897,6 +897,7 @@ set(wesnoth-main_SRC
unit_animation_component.cpp
unit_display.cpp
unit_drawer.cpp
unit_filter.cpp
unit_formula_manager.cpp
unit_frame.cpp
unit_helper.cpp

View file

@ -529,6 +529,7 @@ wesnoth_sources = Split("""
unit_animation_component.cpp
unit_display.cpp
unit_drawer.cpp
unit_filter.cpp
unit_formula_manager.cpp
unit_frame.cpp
unit_helper.cpp

View file

@ -44,6 +44,7 @@
#include "../team.hpp"
#include "../unit.hpp"
#include "../unit_display.hpp"
#include "../unit_filter.hpp"
#include "../variable.hpp"
#include "../whiteboard/manager.hpp"
@ -429,7 +430,11 @@ namespace { // Helpers for get_recalls()
// Only units that match the leader's recall filter are valid.
scoped_recall_unit this_unit("this_unit", save_id, leader_team.recall_list().find_index(recall_unit.id()));
if ( recall_unit.matches_filter(vconfig(leader->recall_filter()), map_location::null_location()) )
const vconfig & rfilter = vconfig(leader->recall_filter());
if ( unit_filter::matches_filter( rfilter,
recall_unit,
map_location::null_location(),
resources::gameboard) )
{
result.push_back(recall_unit_ptr);
if ( already_added != NULL )
@ -527,8 +532,12 @@ namespace { // Helpers for check_recall_location()
team& recall_team = (*resources::teams)[recaller.side()-1];
scoped_recall_unit this_unit("this_unit", recall_team.save_id(),
recall_team.recall_list().find_index(recall_unit.id()));
if ( !recall_unit.matches_filter(vconfig(recaller.recall_filter()),
map_location::null_location()) )
const vconfig & rfilter = vconfig(recaller.recall_filter());
if ( !unit_filter::matches_filter(rfilter,
recall_unit,
map_location::null_location(),
resources::gameboard) )
return RECRUIT_NO_ABLE_LEADER;
// Make sure the unit is on a keep.

View file

@ -24,6 +24,7 @@
#include "ai/lua/core.hpp"
#include "ai/lua/lua_object.hpp"
#include "ai/manager.hpp"
#include "game_board.hpp"
#include "log.hpp"
#include "map_location.hpp"
#include "resources.hpp"
@ -32,6 +33,7 @@
#include "terrain_filter.hpp"
#include "unit.hpp"
#include "unit_map.hpp"
#include "unit_filter.hpp"
#include "wml_exception.hpp"
#include <boost/lexical_cast.hpp>
@ -133,7 +135,7 @@ void target_unit_goal::add_targets(std::back_insert_iterator< std::vector< targe
//find the enemy leaders and explicit targets
BOOST_FOREACH(const unit &u, *resources::units) {
if (u.matches_filter(vconfig(criteria), u.get_location())) {
if (unit_filter::matches_filter(vconfig(criteria), u, u.get_location(), resources::gameboard)) {
LOG_AI_GOAL << "found explicit target unit at ... " << u.get_location() << " with value: " << value() << "\n";
*target_list = target(u.get_location(), value(), target::EXPLICIT);
}
@ -262,7 +264,7 @@ void protect_goal::add_targets(std::back_insert_iterator< std::vector< target >
continue;
}
//TODO: we will protect hidden units, by not testing for invisibility to current side
if (u.matches_filter(vconfig(criteria), u.get_location())) {
if (unit_filter::matches_filter(vconfig(criteria), u, u.get_location(), resources::gameboard)) {
DBG_AI_GOAL << "side " << get_side() << ": in " << goal_type << ": " << u.get_location() << " should be protected\n";
items.insert(u.get_location());
}

View file

@ -35,6 +35,7 @@
#include "../../resources.hpp"
#include "../../team.hpp"
#include "../../tod_manager.hpp"
#include "../../unit_filter.hpp"
#include "../../unit_map.hpp"
#include "../../unit_types.hpp"
#include "../../util.hpp"
@ -251,7 +252,7 @@ void recruitment::execute() {
BOOST_FOREACH(const unit_const_ptr & recall, current_team().recall_list()) {
// Check if this leader is allowed to recall this unit.
vconfig filter = vconfig(leader->recall_filter());
if (!recall->matches_filter(filter, map_location::null_location())) {
if (!unit_filter::matches_filter(filter, *recall, map_location::null_location(), resources::gameboard)) {
continue;
}
data.recruits.insert(recall->type_id());
@ -476,7 +477,7 @@ const std::string* recruitment::get_appropriate_recall(const std::string& type,
}
// Check if this leader is allowed to recall this unit.
vconfig filter = vconfig(leader_data.leader->recall_filter());
if (!recall_unit->matches_filter(filter, map_location::null_location())) {
if (!unit_filter::matches_filter(filter, *recall_unit, map_location::null_location(), resources::gameboard)) {
LOG_AI_RECRUITMENT << "Refused recall because of filter: " << recall_unit->id() << "\n";
continue;
}

View file

@ -29,6 +29,7 @@
#include "../../resources.hpp"
#include "../../unit.hpp"
#include "../../pathfind/pathfind.hpp"
#include "../../unit_filter.hpp"
namespace ai {
@ -75,7 +76,7 @@ boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
std::vector<map_location> unit_locs;
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
if (i->side() == get_side() && i->attacks_left() && !(i->can_recruit() && get_passive_leader())) {
if (!i->matches_filter(vconfig(filter_own_), i->get_location())) {
if (!unit_filter::matches_filter(vconfig(filter_own_), *i, i->get_location(), resources::gameboard)) {
continue;
}
unit_locs.push_back(i->get_location());
@ -98,7 +99,7 @@ boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
if (current_team().is_enemy(j->side()) && !j->incapacitated() &&
!j->invisible(j->get_location()))
{
if (!j->matches_filter(vconfig(filter_enemy_), j->get_location())) {
if (!unit_filter::matches_filter(vconfig(filter_enemy_), *j, j->get_location(), resources::gameboard)) {
continue;
}
map_location adjacent[6];

View file

@ -65,6 +65,7 @@
#include "../unit_animation_component.hpp"
#include "../unit_display.hpp"
#include "../unit_helper.hpp"
#include "../unit_filter.hpp"
#include "../wml_exception.hpp"
#include "../utils/foreach.tpp"
@ -405,7 +406,7 @@ namespace { // Support functions
speaker = units->find(event_info.loc2);
} else if(speaker_str != "narrator") {
for(speaker = units->begin(); speaker != units->end(); ++speaker){
if ( speaker->matches_filter(cfg) )
if ( unit_filter::matches_filter(cfg,*speaker, resources::gameboard) )
break;
}
}
@ -778,7 +779,7 @@ WML_HANDLER_FUNCTION(heal_unit, event_info, cfg)
std::vector<unit*> healers;
if (!healers_filter.null()) {
BOOST_FOREACH(unit& u, *units) {
if ( u.matches_filter(healers_filter) && u.has_ability_type("heals") ) {
if ( unit_filter::matches_filter(healers_filter,u, resources::gameboard) && u.has_ability_type("heals") ) {
healers.push_back(&u);
}
}
@ -799,7 +800,7 @@ WML_HANDLER_FUNCTION(heal_unit, event_info, cfg)
u = units->find(event_info.loc1);
if(!u.valid()) return;
}
else if ( !u->matches_filter(healed_filter) ) continue;
else if ( !unit_filter::matches_filter(healed_filter,*u, resources::gameboard) ) continue;
int heal_amount = u->max_hitpoints() - u->hitpoints();
if(amount.blank() || amount == "full") u->set_hitpoints(u->max_hitpoints());
@ -858,7 +859,7 @@ WML_HANDLER_FUNCTION(kill, event_info, cfg)
secondary_unit = false;
for(unit_map::const_unit_iterator unit = resources::units->begin();
unit != resources::units->end(); ++unit) {
if ( unit->matches_filter(cfg.child("secondary_unit")) )
if ( unit_filter::matches_filter(cfg.child("secondary_unit"), *unit, resources::gameboard) )
{
killer_loc = entity_location(*unit);
secondary_unit = true;
@ -873,7 +874,7 @@ WML_HANDLER_FUNCTION(kill, event_info, cfg)
//Find all the dead units first, because firing events ruins unit_map iteration
std::vector<unit *> dead_men_walking;
BOOST_FOREACH(unit & u, *resources::units){
if ( u.matches_filter(cfg) ) {
if ( unit_filter::matches_filter(cfg,u, resources::gameboard) ) {
dead_men_walking.push_back(&u);
}
}
@ -947,7 +948,7 @@ WML_HANDLER_FUNCTION(kill, event_info, cfg)
{
for(std::vector<unit_ptr>::iterator j = pi->recall_list().begin(); j != pi->recall_list().end();) { //TODO: This block is really messy, cleanup somehow...
scoped_recall_unit auto_store("this_unit", pi->save_id(), j - pi->recall_list().begin());
if ((*j)->matches_filter(cfg, map_location())) {
if (unit_filter::matches_filter(cfg, *(*j), map_location(), resources::gameboard)) {
j = pi->recall_list().erase(j);
} else {
++j;
@ -1415,7 +1416,7 @@ WML_HANDLER_FUNCTION(object, event_info, cfg)
map_location loc;
if(!filter.null()) {
BOOST_FOREACH(const unit &u, *resources::units) {
if ( u.matches_filter(filter) ) {
if ( unit_filter::matches_filter(filter,u, resources::gameboard) ) {
loc = u.get_location();
break;
}
@ -1430,7 +1431,7 @@ WML_HANDLER_FUNCTION(object, event_info, cfg)
std::string command_type = "then";
if ( u != resources::units->end() && (filter.null() || u->matches_filter(filter)) )
if ( u != resources::units->end() && (filter.null() || unit_filter::matches_filter(filter,*u, resources::gameboard)) )
{
///@deprecated This can be removed (and a proper duration=level implemented) after 1.11.2
/// Don't forget to remove it from wmllint too!
@ -1545,7 +1546,7 @@ WML_HANDLER_FUNCTION(recall, /*event_info*/, cfg)
for(std::vector<unit_ptr>::iterator u = avail.begin(); u != avail.end(); ++u) {
DBG_NG << "checking unit against filter...\n";
scoped_recall_unit auto_store("this_unit", player_id, u - avail.begin());
if ((*u)->matches_filter(unit_filter, map_location())) {
if (unit_filter::matches_filter(unit_filter, *(*u), map_location(), resources::gameboard)) {
DBG_NG << (*u)->id() << " matched the filter...\n";
const unit_ptr to_recruit = *u;
const unit* pass_check = to_recruit.get();
@ -1556,8 +1557,8 @@ WML_HANDLER_FUNCTION(recall, /*event_info*/, cfg)
BOOST_FOREACH(unit_map::const_unit_iterator leader, leaders) {
DBG_NG << "...considering " + leader->id() + " as the recalling leader...\n";
map_location loc = cfg_loc;
if ( (leader_filter.null() || leader->matches_filter(leader_filter)) &&
(*u)->matches_filter(vconfig(leader->recall_filter()), map_location()) ) {
if ( (leader_filter.null() || unit_filter::matches_filter(leader_filter, *leader, resources::gameboard)) &&
unit_filter::matches_filter(vconfig(leader->recall_filter()), *(*u),map_location(), resources::gameboard) ) {
DBG_NG << "...matched the leader filter and is able to recall the unit.\n";
if(!resources::gameboard->map().on_board(loc))
loc = leader->get_location();
@ -1725,7 +1726,7 @@ WML_HANDLER_FUNCTION(role, /*event_info*/, cfg)
}
unit_map::iterator itor;
BOOST_FOREACH(unit &u, *resources::units) {
if ( u.matches_filter(filter) ) {
if ( unit_filter::matches_filter(filter,u, resources::gameboard) ) {
u.set_role(cfg["role"]);
found = true;
break;
@ -1762,7 +1763,7 @@ WML_HANDLER_FUNCTION(role, /*event_info*/, cfg)
for(size_t i=0; i < pi->recall_list().size(); ++i) {
unit_ptr u = pi->recall_list()[i];
scoped_recall_unit auto_store("this_unit", player_id, i); //TODO: Should this not be inside the if? Explain me.
if (u->matches_filter(filter, map_location())) {
if (unit_filter::matches_filter(filter, *u, map_location(), resources::gameboard)) {
u->set_role(cfg["role"]);
found=true;
break;
@ -2286,7 +2287,7 @@ WML_HANDLER_FUNCTION(teleport, event_info, cfg)
const vconfig & filter = cfg.child("filter");
if(!filter.null()) {
for (u = resources::units->begin(); u != resources::units->end(); ++u){
if ( u->matches_filter(filter) )
if ( unit_filter::matches_filter(filter,*u, resources::gameboard) )
break;
}
}

View file

@ -21,6 +21,7 @@
#include "conditional_wml.hpp"
#include "../config.hpp"
#include "../game_board.hpp"
#include "../game_data.hpp"
#include "../log.hpp"
#include "../recall_list_manager.hpp"
@ -29,6 +30,7 @@
#include "../team.hpp"
#include "../terrain_filter.hpp"
#include "../unit.hpp"
#include "../unit_filter.hpp"
#include "../unit_map.hpp"
#include "../unit_types.hpp"
#include "../util.hpp"
@ -74,7 +76,7 @@ namespace { // Support functions
int match_count = 0;
BOOST_FOREACH(const unit &i, *resources::units)
{
if ( i.hitpoints() > 0 && i.matches_filter(*u) ) {
if ( i.hitpoints() > 0 && unit_filter::matches_filter(*u,i, resources::gameboard) ) {
++match_count;
if(counts == default_counts) {
// by default a single match is enough, so avoid extra work
@ -95,7 +97,7 @@ namespace { // Support functions
break;
}
scoped_recall_unit auto_store("this_unit", team->save_id(), t);
if ( team->recall_list()[t]->matches_filter(*u) ) {
if ( unit_filter::matches_filter(*u,*team->recall_list()[t], resources::gameboard) ) {
++match_count;
}
}

View file

@ -20,7 +20,10 @@
#include "global.hpp"
#include "entity_location.hpp"
#include "../game_board.hpp"
#include "../resources.hpp"
#include "../unit.hpp"
#include "../unit_filter.hpp"
#include "../variable.hpp"
@ -98,7 +101,7 @@ bool entity_location::matches_unit_filter(const unit_map::const_iterator & un_it
// Filter the unit at the filter location (should be the unit's
// location if no special filter location was specified).
return un_it->matches_filter(filter, filter_loc_) &&
return unit_filter::matches_filter(filter, *un_it, filter_loc_, resources::gameboard) &&
matches_unit(un_it);
}

View file

@ -13,12 +13,14 @@
#include "pathfind/teleport.hpp"
#include "game_board.hpp"
#include "log.hpp"
#include "resources.hpp"
#include "serialization/string_utils.hpp"
#include "team.hpp"
#include "terrain_filter.hpp"
#include "unit.hpp"
#include "unit_filter.hpp"
#include "unit_map.hpp"
#include <boost/foreach.hpp>
@ -74,7 +76,7 @@ void teleport_group::get_teleport_pair(
vconfig filter(cfg_.child_or_empty("filter"), true);
vconfig source(cfg_.child_or_empty("source"), true);
vconfig target(cfg_.child_or_empty("target"), true);
if (u.matches_filter(filter, loc)) {
if (unit_filter::matches_filter(filter, u, loc, resources::gameboard)) {
scoped_xy_unit teleport_unit("teleport_unit", loc.x, loc.y, *resources::units);

View file

@ -98,6 +98,7 @@
#include "tstring.hpp" // for t_string, operator+
#include "unit.hpp" // for unit, intrusive_ptr_add_ref, etc
#include "unit_animation_component.hpp" // for unit_animation_component
#include "unit_filter.hpp"
#include "unit_map.hpp" // for unit_map, etc
#include "unit_ptr.hpp" // for unit_const_ptr, unit_ptr
#include "unit_types.hpp" // for unit_type_data, unit_types, etc
@ -857,7 +858,7 @@ static int intf_get_units(lua_State *L)
for (unit_map::const_unit_iterator ui = units.begin(), ui_end = units.end();
ui != ui_end; ++ui)
{
if (!filter.null() && !ui->matches_filter(filter, ui->get_location()))
if (!filter.null() && !unit_filter::matches_filter(filter, *ui, ui->get_location(), resources::gameboard))
continue;
new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(ui->underlying_id());
lua_pushvalue(L, 1);
@ -894,11 +895,11 @@ static int intf_match_unit(lua_State *L)
team &t = (*resources::teams)[side - 1];
scoped_recall_unit auto_store("this_unit",
t.save_id(), t.recall_list().find_index(u->id()));
lua_pushboolean(L, u->matches_filter(filter, map_location()));
lua_pushboolean(L, unit_filter::matches_filter(filter, *u, map_location(), resources::gameboard));
return 1;
}
lua_pushboolean(L, u->matches_filter(filter, u->get_location()));
lua_pushboolean(L, unit_filter::matches_filter(filter, *u, u->get_location(), resources::gameboard));
return 1;
}
@ -927,7 +928,7 @@ static int intf_get_recall_units(lua_State *L)
if (!filter.null()) {
scoped_recall_unit auto_store("this_unit",
t.save_id(), t.recall_list().find_index(u->id()));
if (!u->matches_filter(filter, map_location()))
if (!unit_filter::matches_filter(filter, *u, map_location(), resources::gameboard))
continue;
}
new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(s, u->underlying_id());
@ -2002,7 +2003,7 @@ static int intf_find_cost_map(lua_State *L)
ui != ui_end; ++ui)
{
bool on_map = ui->get_location().valid();
if (on_map && ui->matches_filter(filter, ui->get_location()))
if (on_map && unit_filter::matches_filter(filter, *ui,ui->get_location(), resources::gameboard))
{
real_units. push_back(&(*ui));
}

View file

@ -17,6 +17,7 @@
#include "global.hpp"
#include "config.hpp"
#include "game_board.hpp"
#include "log.hpp"
#include "recall_list_manager.hpp"
#include "resources.hpp"
@ -26,6 +27,7 @@
#include "serialization/string_utils.hpp"
#include "network.hpp"
#include "unit.hpp"
#include "unit_filter.hpp"
#include "unit_map.hpp"
#include <boost/foreach.hpp>
@ -134,7 +136,7 @@ bool side_filter::match_internal(const team &t) const
if (u.side() != t.side()) {
continue;
}
if (u.matches_filter(unit_filter, u.get_location(), flat_)) {
if (unit_filter::matches_filter(unit_filter, u, u.get_location(), resources::gameboard, flat_)) {
found = true;
break;
}
@ -142,7 +144,7 @@ bool side_filter::match_internal(const team &t) const
if(!found && unit_filter["search_recall_list"].to_bool(false)) {
BOOST_FOREACH(const unit_const_ptr & u, t.recall_list()) {
scoped_recall_unit this_unit("this_unit", t.save_id(),t.recall_list().find_index(u->id()));
if(u->matches_filter(unit_filter, u->get_location(), flat_)) {
if(unit_filter::matches_filter(unit_filter, *u, u->get_location(), resources::gameboard, flat_)) {
found = true;
break;
}

View file

@ -26,6 +26,7 @@
#include "terrain_filter.hpp"
#include "tod_manager.hpp"
#include "unit.hpp"
#include "unit_filter.hpp"
#include "variable.hpp"
#include <boost/foreach.hpp>
@ -153,7 +154,7 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
if(cfg_.has_child("filter")) {
const vconfig& unit_filter = cfg_.child("filter");
const unit_map::const_iterator u = units_.find(loc);
if (u == units_.end() || !u->matches_filter(unit_filter, loc, flat_))
if (u == units_.end() || !unit_filter::matches_filter(unit_filter, *u, loc, resources::gameboard, flat_))
return false;
}

View file

@ -39,6 +39,7 @@
#include "unit_abilities.hpp" // for effect, filter_base_matches
#include "unit_animation.hpp" // for unit_animation
#include "unit_animation_component.hpp" // for unit_animation_component
#include "unit_filter.hpp"
#include "unit_formula_manager.hpp" // for unit_formula_manager
#include "unit_id.hpp"
#include "unit_map.hpp" // for unit_map, etc
@ -1272,411 +1273,6 @@ void unit::remove_ability_by_id(const std::string &ability)
}
}
bool unit::matches_filter(const vconfig& cfg, const map_location& loc, bool use_flat_tod) const
{
bool matches = true;
if(loc.valid()) {
assert(resources::units != NULL);
scoped_xy_unit auto_store("this_unit", loc.x, loc.y, *resources::units);
matches = internal_matches_filter(cfg, loc, use_flat_tod);
} else {
// If loc is invalid, then this is a recall list unit (already been scoped)
matches = internal_matches_filter(cfg, loc, use_flat_tod);
}
// Handle [and], [or], and [not] with in-order precedence
vconfig::all_children_iterator cond = cfg.ordered_begin();
vconfig::all_children_iterator cond_end = cfg.ordered_end();
while(cond != cond_end)
{
const std::string& cond_name = cond.get_key();
const vconfig& cond_filter = cond.get_child();
// Handle [and]
if(cond_name == "and") {
matches = matches && matches_filter(cond_filter,loc,use_flat_tod);
}
// Handle [or]
else if(cond_name == "or") {
matches = matches || matches_filter(cond_filter,loc,use_flat_tod);
}
// Handle [not]
else if(cond_name == "not") {
matches = matches && !matches_filter(cond_filter,loc,use_flat_tod);
}
++cond;
}
return matches;
}
bool unit::internal_matches_filter(const vconfig& cfg, const map_location& loc, bool use_flat_tod) const
{
config::attribute_value cfg_name = cfg["name"];
if (!cfg_name.blank() && cfg_name.str() != name_) {
return false;
}
const config::attribute_value cfg_id = cfg["id"];
if (!cfg_id.blank()) {
const std::string& id = cfg_id;
const std::string& this_id = this->id();
if (id == this_id) {
}
else if ( id.find(',') == std::string::npos ){
return false;
}
else {
const std::vector<std::string>& ids = utils::split(id);
if (std::find(ids.begin(), ids.end(), this_id) == ids.end()) {
return false;
}
}
}
// Allow 'speaker' as an alternative to id, since people use it so often
config::attribute_value cfg_speaker = cfg["speaker"];
if (!cfg_speaker.blank() && cfg_speaker.str() != id()) {
return false;
}
if(cfg.has_child("filter_location")) {
assert(resources::gameboard != NULL);
assert(resources::teams != NULL);
assert(resources::tod_manager != NULL);
assert(resources::units != NULL);
const vconfig& t_cfg = cfg.child("filter_location");
terrain_filter t_filter(t_cfg, *resources::units, use_flat_tod);
if(!t_filter.match(loc)) {
return false;
}
}
const vconfig& filter_side = cfg.child("filter_side");
if(!filter_side.null()) {
side_filter s_filter(filter_side);
if(!s_filter.match(this->side()))
return false;
}
// Also allow filtering on location ranges outside of the location filter
config::attribute_value cfg_x = cfg["x"];
config::attribute_value cfg_y = cfg["y"];
if (!cfg_x.blank() || !cfg_y.blank()){
if(cfg_x == "recall" && cfg_y == "recall") {
//locations on the map are considered to not be on a recall list
if ((!resources::gameboard && loc.valid()) ||
(resources::gameboard && resources::gameboard->map().on_board(loc)))
{
return false;
}
} else if(cfg_x.empty() && cfg_y.empty()) {
return false;
} else if(!loc.matches_range(cfg_x, cfg_y)) {
return false;
}
}
// The type could be a comma separated list of types
config::attribute_value cfg_type = cfg["type"];
if (!cfg_type.blank())
{
const std::string type_ids = cfg_type.str();
const std::string& this_type = type_id();
// We only do the full CSV search if we find a comma in there,
// and if the subsequence is found within the main sequence.
// This is because doing the full CSV split is expensive.
if ( type_ids == this_type ) {
// pass
} else if ( type_ids.find(',') != std::string::npos &&
type_ids.find(this_type) != std::string::npos ) {
const std::vector<std::string>& vals = utils::split(type_ids);
if(std::find(vals.begin(),vals.end(),this_type) == vals.end()) {
return false;
}
} else {
return false;
}
}
// The variation_type could be a comma separated list of types
config::attribute_value cfg_variation_type = cfg["variation"];
if (!cfg_variation_type.blank())
{
const std::string type_ids = cfg_variation_type.str();
const std::string& this_type = variation_;
// We only do the full CSV search if we find a comma in there,
// and if the subsequence is found within the main sequence.
// This is because doing the full CSV split is expensive.
if ( type_ids == this_type ) {
// pass
} else if ( type_ids.find(',') != std::string::npos &&
type_ids.find(this_type) != std::string::npos ) {
const std::vector<std::string>& vals = utils::split(type_ids);
if(std::find(vals.begin(),vals.end(),this_type) == vals.end()) {
return false;
}
} else {
return false;
}
}
// The has_variation_type could be a comma separated list of types
config::attribute_value cfg_has_variation_type = cfg["has_variation"];
if (!cfg_has_variation_type.blank())
{
const std::string& var_ids = cfg_has_variation_type.str();
const std::string& this_var = variation_;
if ( var_ids == this_var ) {
// pass
} else {
bool match = false;
const std::vector<std::string>& variation_types = utils::split(var_ids);
// If this unit is a variation itself then search in the base unit's variations.
const unit_type* const type = this_var.empty() ? type_ : unit_types.find(type_->base_id());
assert(type);
BOOST_FOREACH(const std::string& variation_id, variation_types) {
if (type->has_variation(variation_id)) {
match = true;
break;
}
}
if (!match) return false;
}
}
config::attribute_value cfg_ability = cfg["ability"];
if (!cfg_ability.blank())
{
std::string ability = cfg_ability;
if(has_ability_by_id(ability)) {
// pass
} else if ( ability.find(',') != std::string::npos ) {
const std::vector<std::string>& vals = utils::split(ability);
bool has_ability = false;
for(std::vector<std::string>::const_iterator this_ability = vals.begin(); this_ability != vals.end(); ++this_ability) {
if(has_ability_by_id(*this_ability)) {
has_ability = true;
break;
}
}
if(!has_ability) {
return false;
}
} else {
return false;
}
}
config::attribute_value cfg_race = cfg["race"];
if (!cfg_race.blank()) {
std::string race = cfg_race;
if(race != race_->id()) {
const std::vector<std::string>& vals = utils::split(race);
if(std::find(vals.begin(), vals.end(), race_->id()) == vals.end()) {
return false;
}
}
}
config::attribute_value cfg_gender = cfg["gender"];
if (!cfg_gender.blank() && string_gender(cfg_gender) != gender()) {
return false;
}
config::attribute_value cfg_side = cfg["side"];
if (!cfg_side.blank() && cfg_side.to_int() != side()) {
std::string side = cfg_side;
if ( side.find(',') == std::string::npos ) {
return false;
}
std::vector<std::string> vals = utils::split(side);
if (std::find(vals.begin(), vals.end(), str_cast(side_)) == vals.end()) {
return false;
}
}
config::attribute_value cfg_has_weapon = cfg["has_weapon"];
if (!cfg_has_weapon.blank()) {
std::string weapon = cfg_has_weapon;
bool has_weapon = false;
const std::vector<attack_type>& attacks = this->attacks();
for(std::vector<attack_type>::const_iterator i = attacks.begin();
i != attacks.end(); ++i) {
if(i->id() == weapon) {
has_weapon = true;
break;
}
}
if(!has_weapon) {
return false;
}
}
config::attribute_value cfg_role = cfg["role"];
if (!cfg_role.blank() && cfg_role.str() != role_) {
return false;
}
config::attribute_value cfg_ai_special = cfg["ai_special"];
if (!cfg_ai_special.blank() && ((cfg_ai_special.str() == "guardian") != get_state(STATE_GUARDIAN))) {
return false;
}
config::attribute_value cfg_canrecruit = cfg["canrecruit"];
if (!cfg_canrecruit.blank() && cfg_canrecruit.to_bool() != can_recruit()) {
return false;
}
config::attribute_value cfg_recall_cost = cfg["recall_cost"];
if (!cfg_recall_cost.blank() && cfg_recall_cost.to_int(-1) != recall_cost_) {
return false;
}
config::attribute_value cfg_level = cfg["level"];
if (!cfg_level.blank() && cfg_level.to_int(-1) != level_) {
return false;
}
config::attribute_value cfg_defense = cfg["defense"];
if (!cfg_defense.blank() && cfg_defense.to_int(-1) != defense_modifier(resources::gameboard->map().get_terrain(loc))) {
return false;
}
config::attribute_value cfg_movement = cfg["movement_cost"];
if (!cfg_movement.blank() && cfg_movement.to_int(-1) != movement_cost(resources::gameboard->map().get_terrain(loc))) {
return false;
}
// Now start with the new WML based comparison.
// If a key is in the unit and in the filter, they should match
// filter only => not for us
// unit only => not filtered
const vconfig::child_list& wmlcfgs = cfg.get_children("filter_wml");
if (!wmlcfgs.empty()) {
config unit_cfg;
for (unsigned i = 0; i < wmlcfgs.size(); ++i)
{
config fwml = wmlcfgs[i].get_parsed_config();
/* Check if the filter only cares about variables.
If so, no need to serialize the whole unit. */
config::const_attr_itors ai = fwml.attribute_range();
config::all_children_itors ci = fwml.all_children_range();
if (std::distance(ai.first, ai.second) == 0 &&
std::distance(ci.first, ci.second) == 1 &&
ci.first->key == "variables") {
if (!variables_.matches(ci.first->cfg))
return false;
} else {
if (unit_cfg.empty())
write(unit_cfg);
if (!unit_cfg.matches(fwml))
return false;
}
}
}
if (cfg.has_child("filter_vision")) {
const vconfig::child_list& vis_filt = cfg.get_children("filter_vision");
vconfig::child_list::const_iterator i, i_end = vis_filt.end();
for (i = vis_filt.begin(); i != i_end; ++i) {
bool visible = (*i)["visible"].to_bool(true);
std::set<int> viewers;
// Use standard side filter
side_filter ssf(*i);
std::vector<int> sides = ssf.get_teams();
viewers.insert(sides.begin(), sides.end());
if (viewers.empty()) {
return false;
}
std::set<int>::const_iterator viewer, viewer_end = viewers.end();
for (viewer = viewers.begin(); viewer != viewer_end; ++viewer) {
bool fogged = teams_manager::get_teams()[*viewer - 1].fogged(loc);
bool hiding = this->invisible(loc/*, false(?) */);
bool unit_hidden = fogged || hiding;
if (visible == unit_hidden) return false;
}
}
}
if (cfg.has_child("filter_adjacent")) {
assert(resources::units && resources::gameboard);
const unit_map& units = *resources::units;
map_location adjacent[6];
get_adjacent_tiles(loc, adjacent);
vconfig::child_list::const_iterator i, i_end;
const vconfig::child_list& adj_filt = cfg.get_children("filter_adjacent");
for (i = adj_filt.begin(), i_end = adj_filt.end(); i != i_end; ++i) {
int match_count=0;
config::attribute_value i_adjacent = (*i)["adjacent"];
std::vector<map_location::DIRECTION> dirs = !i_adjacent.blank() ?
map_location::parse_directions(i_adjacent) : map_location::default_dirs();
std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
for (j = dirs.begin(); j != j_end; ++j) {
unit_map::const_iterator unit_itor = units.find(adjacent[*j]);
if (unit_itor == units.end()
|| !unit_itor->matches_filter(*i, unit_itor->get_location(), use_flat_tod)) {
continue;
}
config::attribute_value i_is_enemy = (*i)["is_enemy"];
if (i_is_enemy.blank() || i_is_enemy.to_bool() ==
teams_manager::get_teams()[this->side() - 1].is_enemy(unit_itor->side())) {
++match_count;
}
}
static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6");
config::attribute_value i_count = (*i)["count"];
std::vector<std::pair<int,int> > counts = !i_count.blank()
? utils::parse_ranges(i_count) : default_counts;
if(!in_ranges(match_count, counts)) {
return false;
}
}
}
config::attribute_value cfg_find_in = cfg["find_in"];
if (!cfg_find_in.blank()) {
// Allow filtering by searching a stored variable of units
variable_info vi(cfg_find_in, false, variable_info::TYPE_CONTAINER);
if(!vi.is_valid) return false;
if(vi.explicit_index) {
config::const_child_iterator i = vi.vars->child_range(vi.key).first;
std::advance(i, vi.index);
if ((*i)["id"] != id_) {
return false;
}
} else {
if (!vi.vars->find_child(vi.key, "id", id_))
return false;
}
}
config::attribute_value cfg_formula = cfg["formula"];
if (!cfg_formula.blank()) {
if (!formula_man_->matches_filter(cfg_formula, loc, *this)) {
return false;
}
}
config::attribute_value cfg_lua_function = cfg["lua_function"];
if (!cfg_lua_function.blank()) {
bool b = resources::lua_kernel->run_filter(cfg_lua_function.str().c_str(), *this);
if (!b) return false;
}
return true;
}
void unit::write(config& cfg) const
{
cfg.append(cfg_);
@ -1984,7 +1580,7 @@ void unit::add_modification(const std::string& mod_type, const config& mod, bool
{
// Apply SUF.
if (const config &afilter = effect.child("filter"))
if (!matches_filter(vconfig(afilter), loc_)) continue;
if (!unit_filter::matches_filter(vconfig(afilter), *this, loc_, resources::gameboard)) continue;
const std::string &apply_to = effect["apply_to"];
const std::string &apply_times = effect["times"];

View file

@ -137,6 +137,7 @@ public:
/** The unit type name */
const t_string& type_name() const {return type_name_;}
const std::string& undead_variation() const {return undead_variation_;}
const std::string variation() const {return variation_; }
/** The unit name for display */
const t_string &name() const { return name_; }
@ -241,12 +242,6 @@ public:
bool emits_zoc() const { return emit_zoc_ && !incapacitated();}
bool matches_id(const std::string& unit_id) const;
/* cfg: standard unit filter */
bool matches_filter(const vconfig& cfg,const map_location& loc,bool use_flat_tod=false) const;
/// Determine if *this matches @a filter at its current location.
/// (Only use for units currently on the map; otherwise use the overload
/// that takes a location, possibly with a null location.)
bool matches_filter(const vconfig& filter, bool use_flat_tod=false) const
{ return matches_filter(filter, get_location(), use_flat_tod); }
const std::vector<std::string>& overlays() const { return overlays_; }
void write(config& cfg) const;
@ -382,9 +377,6 @@ protected:
private:
void advance_to(const config &old_cfg, const unit_type &t,
bool use_traits);
bool internal_matches_filter(const vconfig& cfg,const map_location& loc,
bool use_flat_tod) const;
/*
* cfg: an ability WML structure
*/
@ -393,7 +385,10 @@ private:
bool ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const;
bool resistance_filter_matches(const config& cfg,bool attacker,const std::string& damage_name, int res) const;
public:
bool has_ability_by_id(const std::string& ability) const;
// ^ Needed for unit_filter
private:
void remove_ability_by_id(const std::string& ability);
/** register a trait's name and its description for UI's use*/

View file

@ -17,12 +17,14 @@
* Manage unit-abilities, like heal, cure, and weapon_specials.
*/
#include "game_board.hpp"
#include "log.hpp"
#include "resources.hpp"
#include "team.hpp"
#include "terrain_filter.hpp"
#include "unit.hpp"
#include "unit_abilities.hpp"
#include "unit_filter.hpp"
#include "unit_map.hpp"
#include <boost/foreach.hpp>
@ -295,7 +297,7 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
assert(resources::units && resources::gameboard && resources::teams && resources::tod_manager);
if (const config &afilter = cfg.child("filter"))
if ( !matches_filter(vconfig(afilter), loc, illuminates) )
if ( !unit_filter::matches_filter(vconfig(afilter), *this, loc, resources::gameboard, illuminates) )
return false;
map_location adjacent[6];
@ -313,7 +315,7 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
unit_map::const_iterator unit = units.find(adjacent[index]);
if (unit == units.end())
return false;
if (!unit->matches_filter(vconfig(i), unit->get_location(), illuminates))
if (!unit_filter::matches_filter(vconfig(i), *unit, unit->get_location(), resources::gameboard, illuminates))
return false;
}
}
@ -351,7 +353,7 @@ bool unit::ability_affects_adjacent(const std::string& ability, const config& cf
std::vector<std::string> dirs = utils::split(i["adjacent"]);
if(std::find(dirs.begin(),dirs.end(),adjacent_names[dir]) != dirs.end()) {
if (const config &filter = i.child("filter")) {
if ( matches_filter(vconfig(filter), loc, illuminates) )
if ( unit_filter::matches_filter(vconfig(filter), *this, loc, resources::gameboard, illuminates) )
return true;
} else
return true;
@ -369,7 +371,7 @@ bool unit::ability_affects_self(const std::string& ability,const config& cfg,con
const config &filter = cfg.child("filter_self");
bool affect_self = cfg["affect_self"].to_bool(true);
if (!filter || !affect_self) return affect_self;
return matches_filter(vconfig(filter), loc, ability == "illuminates");
return unit_filter::matches_filter(vconfig(filter), *this, loc, resources::gameboard, ability == "illuminates");
}
bool unit::has_ability_type(const std::string& ability) const
@ -792,7 +794,7 @@ namespace { // Helpers for attack_type::special_active()
return true;
// Check for a unit match.
if ( !un_it.valid() || !un_it->matches_filter(vconfig(filter_child), loc) )
if ( !un_it.valid() || !unit_filter::matches_filter(vconfig(filter_child), *un_it, loc, resources::gameboard) )
return false;
// Check for a weapon match.
@ -882,7 +884,7 @@ bool attack_type::special_active(const config& special, AFFECTS whom,
continue;
unit_map::const_iterator unit = units.find(adjacent[index]);
if ( unit == units.end() ||
!unit->matches_filter(vconfig(i), adjacent[index]) )
!unit_filter::matches_filter(vconfig(i), *unit, adjacent[index], resources::gameboard) )
return false;
}
}

View file

@ -23,6 +23,7 @@
#include "resources.hpp"
#include "unit.hpp"
#include "unit_animation_component.hpp"
#include "unit_filter.hpp"
#include "variable.hpp"
#include <boost/foreach.hpp>
@ -368,7 +369,7 @@ unit_animation::unit_animation(const config& cfg,const std::string& frame_string
}
int unit_animation::matches(const display &disp,const map_location& loc,const map_location& second_loc, const unit* my_unit,const std::string & event,const int value,hit_type hit,const attack_type* attack,const attack_type* second_attack, int value2) const
int unit_animation::matches(const display &disp, const map_location& loc,const map_location& second_loc, const unit* my_unit,const std::string & event,const int value,hit_type hit,const attack_type* attack,const attack_type* second_attack, int value2) const
{
int result = base_score_;
if(!event.empty()&&!event_.empty()) {
@ -403,7 +404,7 @@ int unit_animation::matches(const display &disp,const map_location& loc,const ma
}
std::vector<config>::const_iterator myitor;
for(myitor = unit_filter_.begin(); myitor != unit_filter_.end(); ++myitor) {
if (!my_unit->matches_filter(vconfig(*myitor), loc)) return MATCH_FAIL;
if (!unit_filter::matches_filter(vconfig(*myitor), *my_unit, loc, &disp.get_disp_context())) return MATCH_FAIL;
++result;
}
if(!secondary_unit_filter_.empty()) {
@ -412,7 +413,7 @@ int unit_animation::matches(const display &disp,const map_location& loc,const ma
if (unit->get_location() == second_loc) {
std::vector<config>::const_iterator second_itor;
for(second_itor = secondary_unit_filter_.begin(); second_itor != secondary_unit_filter_.end(); ++second_itor) {
if (!unit->matches_filter(vconfig(*second_itor), second_loc)) return MATCH_FAIL;
if (!unit_filter::matches_filter(vconfig(*second_itor), *unit, second_loc, &disp.get_disp_context())) return MATCH_FAIL;
result++;
}

View file

@ -28,6 +28,7 @@
#include "terrain_filter.hpp"
#include "unit.hpp"
#include "unit_animation_component.hpp"
#include "unit_filter.hpp"
#include "unit_map.hpp"
#include <boost/foreach.hpp>
@ -773,7 +774,7 @@ void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const m
vconfig filter = cfg.child("filter");
if(!filter.null()) {
for (u = resources::units->begin(); u != resources::units->end(); ++u) {
if ( u->matches_filter(filter) )
if ( unit_filter::matches_filter(filter, *u, resources::gameboard) )
break;
}
}

447
src/unit_filter.cpp Normal file
View file

@ -0,0 +1,447 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
#include "unit_filter.hpp"
#include "global.hpp"
#include "config.hpp"
#include "display_context.hpp"
#include "map_location.hpp"
#include "resources.hpp" //Needed for lua kernel pointer
#include "scripting/lua.hpp" //Needed for lua kernel
#include "side_filter.hpp"
#include "team.hpp"
#include "terrain_filter.hpp"
#include "unit.hpp"
#include "unit_formula_manager.hpp"
#include "unit_map.hpp"
#include "unit_types.hpp"
#include "variable.hpp" // needed for vconfig, scoped unit
#include <boost/foreach.hpp>
namespace { bool internal_matches_filter(const vconfig& filter, const unit & u, const map_location& loc, const display_context * board, bool use_flat_tod); }
namespace unit_filter {
bool matches_filter(const vconfig& filter, const unit & u, const display_context * board, bool use_flat_tod)
{ return matches_filter(filter, u, u.get_location(), board, use_flat_tod); }
bool matches_filter(const vconfig& cfg, const unit & u, const map_location& loc, const display_context * board, bool use_flat_tod)
{
bool matches = true;
if(loc.valid()) {
assert(board != NULL);
scoped_xy_unit auto_store("this_unit", loc.x, loc.y, board->units());
matches = internal_matches_filter(cfg, u, loc, board, use_flat_tod);
} else {
// If loc is invalid, then this is a recall list unit (already been scoped)
matches = internal_matches_filter(cfg, u, loc, board, use_flat_tod);
}
// Handle [and], [or], and [not] with in-order precedence
vconfig::all_children_iterator cond = cfg.ordered_begin();
vconfig::all_children_iterator cond_end = cfg.ordered_end();
while(cond != cond_end)
{
const std::string& cond_name = cond.get_key();
const vconfig& cond_filter = cond.get_child();
// Handle [and]
if(cond_name == "and") {
matches = matches && matches_filter(cond_filter,u, loc, board, use_flat_tod);
}
// Handle [or]
else if(cond_name == "or") {
matches = matches || matches_filter(cond_filter,u, loc, board,use_flat_tod);
}
// Handle [not]
else if(cond_name == "not") {
matches = matches && !matches_filter(cond_filter,u, loc, board,use_flat_tod);
}
++cond;
}
return matches;
}
} //end namespace unit_filter
namespace { //begin anonymous namespace
bool internal_matches_filter(const vconfig& cfg, const unit & u, const map_location& loc, const display_context * board, bool use_flat_tod)
{
config::attribute_value cfg_name = cfg["name"];
if (!cfg_name.blank() && cfg_name.str() != u.name()) {
return false;
}
const config::attribute_value cfg_id = cfg["id"];
if (!cfg_id.blank()) {
const std::string& id = cfg_id;
const std::string& this_id = u.id();
if (id == this_id) {
}
else if ( id.find(',') == std::string::npos ){
return false;
}
else {
const std::vector<std::string>& ids = utils::split(id);
if (std::find(ids.begin(), ids.end(), this_id) == ids.end()) {
return false;
}
}
}
// Allow 'speaker' as an alternative to id, since people use it so often
config::attribute_value cfg_speaker = cfg["speaker"];
if (!cfg_speaker.blank() && cfg_speaker.str() != u.id()) {
return false;
}
if(cfg.has_child("filter_location")) {
assert(board != NULL);
const vconfig& t_cfg = cfg.child("filter_location");
terrain_filter t_filter(t_cfg, board->units(), use_flat_tod);
if(!t_filter.match(loc)) {
return false;
}
}
const vconfig& filter_side = cfg.child("filter_side");
if(!filter_side.null()) {
side_filter s_filter(filter_side);
if(!s_filter.match(u.side()))
return false;
}
// Also allow filtering on location ranges outside of the location filter
config::attribute_value cfg_x = cfg["x"];
config::attribute_value cfg_y = cfg["y"];
if (!cfg_x.blank() || !cfg_y.blank()){
if(cfg_x == "recall" && cfg_y == "recall") {
//locations on the map are considered to not be on a recall list
if ((!board && loc.valid()) ||
(board && board->map().on_board(loc)))
{
return false;
}
} else if(cfg_x.empty() && cfg_y.empty()) {
return false;
} else if(!loc.matches_range(cfg_x, cfg_y)) {
return false;
}
}
// The type could be a comma separated list of types
config::attribute_value cfg_type = cfg["type"];
if (!cfg_type.blank())
{
const std::string type_ids = cfg_type.str();
const std::string& this_type = u.type_id();
// We only do the full CSV search if we find a comma in there,
// and if the subsequence is found within the main sequence.
// This is because doing the full CSV split is expensive.
if ( type_ids == this_type ) {
// pass
} else if ( type_ids.find(',') != std::string::npos &&
type_ids.find(this_type) != std::string::npos ) {
const std::vector<std::string>& vals = utils::split(type_ids);
if(std::find(vals.begin(),vals.end(),this_type) == vals.end()) {
return false;
}
} else {
return false;
}
}
// The variation_type could be a comma separated list of types
config::attribute_value cfg_variation_type = cfg["variation"];
if (!cfg_variation_type.blank())
{
const std::string type_ids = cfg_variation_type.str();
const std::string& this_type = u.variation();
// We only do the full CSV search if we find a comma in there,
// and if the subsequence is found within the main sequence.
// This is because doing the full CSV split is expensive.
if ( type_ids == this_type ) {
// pass
} else if ( type_ids.find(',') != std::string::npos &&
type_ids.find(this_type) != std::string::npos ) {
const std::vector<std::string>& vals = utils::split(type_ids);
if(std::find(vals.begin(),vals.end(),this_type) == vals.end()) {
return false;
}
} else {
return false;
}
}
// The has_variation_type could be a comma separated list of types
config::attribute_value cfg_has_variation_type = cfg["has_variation"];
if (!cfg_has_variation_type.blank())
{
const std::string& var_ids = cfg_has_variation_type.str();
const std::string& this_var = u.variation();
if ( var_ids == this_var ) {
// pass
} else {
bool match = false;
const std::vector<std::string>& variation_types = utils::split(var_ids);
// If this unit is a variation itself then search in the base unit's variations.
const unit_type* const type = this_var.empty() ? &u.type() : unit_types.find(u.type().base_id());
assert(type);
BOOST_FOREACH(const std::string& variation_id, variation_types) {
if (type->has_variation(variation_id)) {
match = true;
break;
}
}
if (!match) return false;
}
}
config::attribute_value cfg_ability = cfg["ability"];
if (!cfg_ability.blank())
{
std::string ability = cfg_ability;
if(u.has_ability_by_id(ability)) {
// pass
} else if ( ability.find(',') != std::string::npos ) {
const std::vector<std::string>& vals = utils::split(ability);
bool has_ability = false;
for(std::vector<std::string>::const_iterator this_ability = vals.begin(); this_ability != vals.end(); ++this_ability) {
if(u.has_ability_by_id(*this_ability)) {
has_ability = true;
break;
}
}
if(!has_ability) {
return false;
}
} else {
return false;
}
}
config::attribute_value cfg_race = cfg["race"];
if (!cfg_race.blank()) {
std::string race = cfg_race;
if(race != u.race()->id()) {
const std::vector<std::string>& vals = utils::split(race);
if(std::find(vals.begin(), vals.end(), u.race()->id()) == vals.end()) {
return false;
}
}
}
config::attribute_value cfg_gender = cfg["gender"];
if (!cfg_gender.blank() && string_gender(cfg_gender) != u.gender()) {
return false;
}
config::attribute_value cfg_side = cfg["side"];
if (!cfg_side.blank() && cfg_side.to_int() != u.side()) {
std::string side = cfg_side;
if ( side.find(',') == std::string::npos ) {
return false;
}
std::vector<std::string> vals = utils::split(side);
if (std::find(vals.begin(), vals.end(), str_cast(u.side())) == vals.end()) {
return false;
}
}
config::attribute_value cfg_has_weapon = cfg["has_weapon"];
if (!cfg_has_weapon.blank()) {
std::string weapon = cfg_has_weapon;
bool has_weapon = false;
const std::vector<attack_type>& attacks = u.attacks();
for(std::vector<attack_type>::const_iterator i = attacks.begin();
i != attacks.end(); ++i) {
if(i->id() == weapon) {
has_weapon = true;
break;
}
}
if(!has_weapon) {
return false;
}
}
config::attribute_value cfg_role = cfg["role"];
if (!cfg_role.blank() && cfg_role.str() != u.get_role()) {
return false;
}
config::attribute_value cfg_ai_special = cfg["ai_special"];
if (!cfg_ai_special.blank() && ((cfg_ai_special.str() == "guardian") != u.get_state(unit::STATE_GUARDIAN))) {
return false;
}
config::attribute_value cfg_canrecruit = cfg["canrecruit"];
if (!cfg_canrecruit.blank() && cfg_canrecruit.to_bool() != u.can_recruit()) {
return false;
}
config::attribute_value cfg_recall_cost = cfg["recall_cost"];
if (!cfg_recall_cost.blank() && cfg_recall_cost.to_int(-1) != u.recall_cost()) {
return false;
}
config::attribute_value cfg_level = cfg["level"];
if (!cfg_level.blank() && cfg_level.to_int(-1) != u.level()) {
return false;
}
config::attribute_value cfg_defense = cfg["defense"];
if (!cfg_defense.blank() && cfg_defense.to_int(-1) != u.defense_modifier(board->map().get_terrain(loc))) {
return false;
}
config::attribute_value cfg_movement = cfg["movement_cost"];
if (!cfg_movement.blank() && cfg_movement.to_int(-1) != u.movement_cost(board->map().get_terrain(loc))) {
return false;
}
// Now start with the new WML based comparison.
// If a key is in the unit and in the filter, they should match
// filter only => not for us
// unit only => not filtered
const vconfig::child_list& wmlcfgs = cfg.get_children("filter_wml");
if (!wmlcfgs.empty()) {
config unit_cfg;
for (unsigned i = 0; i < wmlcfgs.size(); ++i)
{
config fwml = wmlcfgs[i].get_parsed_config();
/* Check if the filter only cares about variables.
If so, no need to serialize the whole unit. */
config::const_attr_itors ai = fwml.attribute_range();
config::all_children_itors ci = fwml.all_children_range();
if (std::distance(ai.first, ai.second) == 0 &&
std::distance(ci.first, ci.second) == 1 &&
ci.first->key == "variables") {
if (!u.variables().matches(ci.first->cfg))
return false;
} else {
if (unit_cfg.empty())
u.write(unit_cfg);
if (!unit_cfg.matches(fwml))
return false;
}
}
}
if (cfg.has_child("filter_vision")) {
const vconfig::child_list& vis_filt = cfg.get_children("filter_vision");
vconfig::child_list::const_iterator i, i_end = vis_filt.end();
for (i = vis_filt.begin(); i != i_end; ++i) {
bool visible = (*i)["visible"].to_bool(true);
std::set<int> viewers;
// Use standard side filter
side_filter ssf(*i);
std::vector<int> sides = ssf.get_teams();
viewers.insert(sides.begin(), sides.end());
if (viewers.empty()) {
return false;
}
std::set<int>::const_iterator viewer, viewer_end = viewers.end();
for (viewer = viewers.begin(); viewer != viewer_end; ++viewer) {
bool fogged = board->teams()[*viewer - 1].fogged(loc);
bool hiding = u.invisible(loc/*, false(?) */);
bool unit_hidden = fogged || hiding;
if (visible == unit_hidden) return false;
}
}
}
if (cfg.has_child("filter_adjacent")) {
assert(board);
const unit_map& units = board->units();
map_location adjacent[6];
get_adjacent_tiles(loc, adjacent);
vconfig::child_list::const_iterator i, i_end;
const vconfig::child_list& adj_filt = cfg.get_children("filter_adjacent");
for (i = adj_filt.begin(), i_end = adj_filt.end(); i != i_end; ++i) {
int match_count=0;
config::attribute_value i_adjacent = (*i)["adjacent"];
std::vector<map_location::DIRECTION> dirs = !i_adjacent.blank() ?
map_location::parse_directions(i_adjacent) : map_location::default_dirs();
std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
for (j = dirs.begin(); j != j_end; ++j) {
unit_map::const_iterator unit_itor = units.find(adjacent[*j]);
if (unit_itor == units.end()
|| !unit_filter::matches_filter(*i, *unit_itor, unit_itor->get_location(), board, use_flat_tod)) {
continue;
}
config::attribute_value i_is_enemy = (*i)["is_enemy"];
if (i_is_enemy.blank() || i_is_enemy.to_bool() ==
board->teams()[u.side() - 1].is_enemy(unit_itor->side())) {
++match_count;
}
}
static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6");
config::attribute_value i_count = (*i)["count"];
std::vector<std::pair<int,int> > counts = !i_count.blank()
? utils::parse_ranges(i_count) : default_counts;
if(!in_ranges(match_count, counts)) {
return false;
}
}
}
config::attribute_value cfg_find_in = cfg["find_in"];
if (!cfg_find_in.blank()) {
// Allow filtering by searching a stored variable of units
variable_info vi(cfg_find_in, false, variable_info::TYPE_CONTAINER);
if(!vi.is_valid) return false;
if(vi.explicit_index) {
config::const_child_iterator i = vi.vars->child_range(vi.key).first;
std::advance(i, vi.index);
if ((*i)["id"] != u.id()) {
return false;
}
} else {
if (!vi.vars->find_child(vi.key, "id", u.id()))
return false;
}
}
config::attribute_value cfg_formula = cfg["formula"];
if (!cfg_formula.blank()) {
if (!u.formula_manager().matches_filter(cfg_formula, loc, u)) {
return false;
}
}
config::attribute_value cfg_lua_function = cfg["lua_function"];
if (!cfg_lua_function.blank()) {
bool b = resources::lua_kernel->run_filter(cfg_lua_function.str().c_str(), u);
if (!b) return false;
}
return true;
}
} //end anonymous namespace

39
src/unit_filter.hpp Normal file
View file

@ -0,0 +1,39 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* This namespace contains the function that checks if a unit matches
* a filter. It helps by simplifying the unit object (which before now
* holds the "match" function).
*
* TODO:
* Make a class that abstracts a unit filter, assembles the constituent
* side filters and terrain filters and conditional filters, and caches
* these to speed up repeated application of the filter.
*/
class display_context;
class unit;
class vconfig;
struct map_location;
namespace unit_filter {
bool matches_filter(const vconfig& cfg,const unit & u, const map_location& loc, const display_context * board, bool use_flat_tod=false);
/// Determine if *this matches @a filter at its current location.
/// (Only use for units currently on the map; otherwise use the overload
/// that takes a location, possibly with a null location.)
bool matches_filter(const vconfig& filter, const unit & u, const display_context* board, bool use_flat_tod=false);
}