Merge branch 'refactor_unit_filter'

This commit is contained in:
Chris Beck 2014-07-03 19:27:36 -04:00
commit cee6874786
44 changed files with 902 additions and 587 deletions

View file

@ -71,7 +71,8 @@
code =<<
wesnoth.set_variable("rnd_num", math.random(200))
>>
[/lua])}
[/lua]
)}
{TEST_BREAK_REPLAY "fixed_lua_random_replay_with_sync_choice"
([lua]
@ -82,4 +83,5 @@
end)
wesnoth.set_variable("rnd_num", result.value)
>>
[/lua])}
[/lua]
)}

View file

@ -189,7 +189,8 @@
name=post_advance
{VARIABLE pass_test 1}
[/my_event]
[/variables])}
[/variables]
)}
[store_unit]
[filter]
x=1

View file

@ -61,7 +61,8 @@
id=bob
x={STOP_X}
y={STOP_Y}
[/have_unit])}
[/have_unit]
)}
[/event]
[/test]
#enddef

View file

@ -9,14 +9,16 @@
[have_unit]
x,y=9,5
[/have_unit]
[/not])}
[/not]
)}
#enddef
#define ASSERT_YES_9_5
{ASSERT (
[have_unit]
x,y=9,5
[/have_unit])}
[/have_unit]
)}
#enddef

View file

@ -20,13 +20,15 @@
side = 1
search_recall_list = no
[/have_unit]
[/not])}
[/not]
)}
{RETURN (
[have_unit]
id = Charlie
canrecruit = no
side = 1
search_recall_list = yes
[/have_unit])}
[/have_unit]
)}
[/event]
)}

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

@ -25,6 +25,7 @@
#include "../config.hpp"
#include "../config_assign.hpp"
#include "../filter_context.hpp"
#include "../game_board.hpp"
#include "../game_display.hpp"
#include "../game_events/pump.hpp"
@ -44,6 +45,7 @@
#include "../team.hpp"
#include "../unit.hpp"
#include "../unit_display.hpp"
#include "../unit_filter.hpp"
#include "../variable.hpp"
#include "../whiteboard/manager.hpp"
@ -429,7 +431,8 @@ 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 unit_filter ufilt(vconfig(leader->recall_filter()), resources::filter_con);
if ( ufilt(recall_unit, map_location::null_location()) )
{
result.push_back(recall_unit_ptr);
if ( already_added != NULL )
@ -527,8 +530,9 @@ 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 unit_filter ufilt(vconfig(recaller.recall_filter()), resources::filter_con);
if ( !ufilt(recall_unit, map_location::null_location()) )
return RECRUIT_NO_ABLE_LEADER;
// Make sure the unit is on a keep.

View file

@ -24,6 +24,8 @@
#include "ai/lua/core.hpp"
#include "ai/lua/lua_object.hpp"
#include "ai/manager.hpp"
#include "filter_context.hpp"
#include "game_board.hpp"
#include "log.hpp"
#include "map_location.hpp"
#include "resources.hpp"
@ -32,6 +34,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>
@ -132,8 +135,9 @@ void target_unit_goal::add_targets(std::back_insert_iterator< std::vector< targe
if (!criteria) return;
//find the enemy leaders and explicit targets
const unit_filter ufilt(vconfig(criteria), resources::filter_con);
BOOST_FOREACH(const unit &u, *resources::units) {
if (u.matches_filter(vconfig(criteria), u.get_location())) {
if (ufilt( u )) {
LOG_AI_GOAL << "found explicit target unit at ... " << u.get_location() << " with value: " << value() << "\n";
*target_list = target(u.get_location(), value(), target::EXPLICIT);
}
@ -163,7 +167,7 @@ void target_location_goal::on_create()
}
const config &criteria = cfg_.child("criteria");
if (criteria) {
filter_ptr_ = boost::shared_ptr<terrain_filter>(new terrain_filter(vconfig(criteria),*resources::units));
filter_ptr_ = boost::shared_ptr<terrain_filter>(new terrain_filter(vconfig(criteria),resources::filter_con));
}
}
@ -219,7 +223,7 @@ void protect_goal::on_create()
}
const config &criteria = cfg_.child("criteria");
if (criteria) {
filter_ptr_ = boost::shared_ptr<terrain_filter>(new terrain_filter(vconfig(criteria),*resources::units));
filter_ptr_ = boost::shared_ptr<terrain_filter>(new terrain_filter(vconfig(criteria),resources::filter_con));
}
@ -256,13 +260,14 @@ void protect_goal::add_targets(std::back_insert_iterator< std::vector< target >
std::set<map_location> items;
if (protect_unit_) {
const unit_filter ufilt(vconfig(criteria), resources::filter_con);
BOOST_FOREACH(const unit &u, units)
{
if (protect_only_own_unit_ && u.side() != get_side()) {
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 (ufilt(u)) {
DBG_AI_GOAL << "side " << get_side() << ": in " << goal_type << ": " << u.get_location() << " should be protected\n";
items.insert(u.get_location());
}

View file

@ -179,10 +179,10 @@ public:
static terrain_filter cfg_to_value(const config &cfg)
{
if (const config &v = cfg.child("value")) {
return terrain_filter(vconfig(v), *resources::units);
return terrain_filter(vconfig(v), resources::filter_con);
}
static config c("not");
return terrain_filter(vconfig(c),*resources::units);
return terrain_filter(vconfig(c),resources::filter_con);
}
static void cfg_to_value(const config &cfg, terrain_filter &value)
@ -442,7 +442,7 @@ public:
static terrain_filter variant_to_value(const variant &var)
{
static config c("not");
terrain_filter value(vconfig(c),*resources::units);
terrain_filter value(vconfig(c),resources::filter_con);
variant_to_value(var,value);
return value;
}

View file

@ -608,7 +608,7 @@ const terrain_filter& readonly_context_impl::get_avoid() const
}
config cfg;
cfg.add_child("not");
static terrain_filter tf(vconfig(cfg),*resources::units);
static terrain_filter tf(vconfig(cfg),resources::filter_con);
return tf;
}

View file

@ -138,7 +138,7 @@ inline boost::shared_ptr<terrain_filter> lua_object<terrain_filter>::to_type(lua
boost::shared_ptr<config> cfg = boost::shared_ptr<config>(new config());
boost::shared_ptr<vconfig> vcfg = boost::shared_ptr<vconfig>(new vconfig(*cfg));
luaW_tovconfig(L, n, *vcfg);
boost::shared_ptr<terrain_filter> tf = boost::shared_ptr<terrain_filter>(new terrain_filter(*vcfg, *resources::units));
boost::shared_ptr<terrain_filter> tf = boost::shared_ptr<terrain_filter>(new terrain_filter(*vcfg, resources::filter_con));
return tf;
}

View file

@ -25,6 +25,7 @@
#include "../manager.hpp"
#include "../../actions/attack.hpp"
#include "../../attack_prediction.hpp"
#include "../../filter_context.hpp"
#include "../../game_board.hpp"
#include "../../game_display.hpp"
#include "../../log.hpp"
@ -35,6 +36,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"
@ -250,8 +252,8 @@ void recruitment::execute() {
// we'll check if we can do a recall instead of a recruitment.
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())) {
const unit_filter ufilt( vconfig(leader->recall_filter()), resources::filter_con);
if (!ufilt(*recall, map_location::null_location())) {
continue;
}
data.recruits.insert(recall->type_id());
@ -475,8 +477,8 @@ const std::string* recruitment::get_appropriate_recall(const std::string& type,
continue;
}
// 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())) {
const unit_filter ufilt(vconfig(leader_data.leader->recall_filter()), resources::filter_con);
if (!ufilt(*recall_unit, map_location::null_location())) {
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 {
@ -73,9 +74,10 @@ boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
unit_map& units_ = *resources::units;
std::vector<map_location> unit_locs;
const unit_filter filt_own(vconfig(filter_own_), resources::filter_con);
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 (!filt_own(*i)) {
continue;
}
unit_locs.push_back(i->get_location());
@ -91,25 +93,26 @@ boost::shared_ptr<attacks_vector> aspect_attacks::analyze_targets() const
unit_stats_cache().clear();
const unit_filter filt_en(vconfig(filter_enemy_), resources::filter_con);
for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
// Attack anyone who is on the enemy side,
// and who is not invisible or petrified.
if (current_team().is_enemy(j->side()) && !j->incapacitated() &&
!j->invisible(j->get_location()))
{
if (!j->matches_filter(vconfig(filter_enemy_), j->get_location())) {
continue;
}
map_location adjacent[6];
get_adjacent_tiles(j->get_location(), adjacent);
attack_analysis analysis;
analysis.target = j->get_location();
analysis.vulnerability = 0.0;
analysis.support = 0.0;
do_attack_analysis(j->get_location(), srcdst, dstsrc,
fullmove_srcdst, fullmove_dstsrc, enemy_srcdst, enemy_dstsrc,
adjacent,used_locations,unit_locs,*res,analysis, current_team());
// Attack anyone who is on the enemy side,
// and who is not invisible or petrified.
if (current_team().is_enemy(j->side()) && !j->incapacitated() &&
!j->invisible(j->get_location()))
{
if (!filt_en( *j)) {
continue;
}
map_location adjacent[6];
get_adjacent_tiles(j->get_location(), adjacent);
attack_analysis analysis;
analysis.target = j->get_location();
analysis.vulnerability = 0.0;
analysis.support = 0.0;
do_attack_analysis(j->get_location(), srcdst, dstsrc,
fullmove_srcdst, fullmove_dstsrc, enemy_srcdst, enemy_dstsrc,
adjacent,used_locations,unit_locs,*res,analysis, current_team());
}
}
return res;

View file

@ -416,6 +416,16 @@ const time_of_day & display::get_time_of_day(const map_location& /*loc*/) const
return tod;
}
/**
* Display objects don't hold a tod maanger, instead game_display objects do. If the base version of this method is called,
* try to get it from resources and use an assert to check for failure.
*/
const tod_manager & display::get_tod_man() const
{
assert(resources::tod_manager);
return *resources::tod_manager;
}
void display::update_tod() {
const time_of_day& tod = get_time_of_day();
tod_color col = color_adjust_ + tod.color;

View file

@ -49,6 +49,7 @@ namespace wb {
#include "animated.hpp"
#include "display_context.hpp"
#include "filter_context.hpp"
#include "font.hpp"
#include "image.hpp" //only needed for enums (!)
#include "key.hpp"
@ -71,7 +72,7 @@ namespace wb {
class gamemap;
class display
class display : public filter_context
{
public:
display(const display_context * dc, CVideo& video, boost::weak_ptr<wb::manager> wb,
@ -164,6 +165,7 @@ public:
void change_display_context(const display_context * dc);
const display_context & get_disp_context() const { return *dc_; }
virtual const tod_manager & get_tod_man() const; //!< This is implemented properly in game_display. The display:: impl could be pure virtual here but we decide not to.
void reset_halo_manager();
void reset_halo_manager(halo::manager & hm);

43
src/filter_context.hpp Normal file
View file

@ -0,0 +1,43 @@
/*
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 class is an abstract base class which represents a display context
* (game map, units, and teams) together with a TOD manager. This, plus
* a lua kernel (currently a singleton) is sufficient to evaluate filters.
*
**/
#ifndef FILTER_CONTEXT_HPP_INCLUDED
#define FILTER_CONTEXT_HPP_INCLUDED
#include <vector>
class display_context;
class tod_manager;
class filter_context {
public:
// accessors
virtual const display_context & get_disp_context() const = 0;
virtual const tod_manager & get_tod_man() const = 0;
// Dtor
virtual ~filter_context() {}
};
#endif

View file

@ -60,7 +60,7 @@ class game_board : public display_context {
friend class play_controller;
friend class events::mouse_handler;
friend class events::menu_handler;
friend struct game_state;
friend class game_state;
/**
* Temporary unit move structs:

View file

@ -130,6 +130,8 @@ public:
bool has_time_area() const;
const tod_manager & get_tod_man() const { return tod_manager_; } //<! Allows this class to properly implement filter context, used for animations
protected:
/**
* game_display pre_draw does specific things related e.g. to unit rendering

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"
@ -374,11 +375,11 @@ namespace { // Support functions
if (!ssf.null()) {
if(!sides.empty()) { WRN_NG << "ignoring duplicate side filter information (inline side=)" << std::endl; }
side_filter filter(ssf);
side_filter filter(ssf, resources::filter_con);
return filter.get_teams();
}
side_filter filter(sides.str());
side_filter filter(sides.str(), resources::filter_con);
return filter.get_teams();
}
@ -404,8 +405,9 @@ namespace { // Support functions
} else if(speaker_str == "second_unit") {
speaker = units->find(event_info.loc2);
} else if(speaker_str != "narrator") {
const unit_filter ufilt(cfg, resources::filter_con);
for(speaker = units->begin(); speaker != units->end(); ++speaker){
if ( speaker->matches_filter(cfg) )
if ( ufilt(*speaker) )
break;
}
}
@ -443,12 +445,12 @@ namespace { // Support functions
{
// Filter the sides.
const vconfig &ssf = cfg.child("filter_side");
const side_filter s_filter(ssf.null() ? vconfig::empty_vconfig() : ssf);
const side_filter s_filter(ssf.null() ? vconfig::empty_vconfig() : ssf, resources::filter_con);
const std::vector<int> sides = s_filter.get_teams();
// Filter the locations.
std::set<map_location> locs;
const terrain_filter t_filter(cfg, *resources::units);
const terrain_filter t_filter(cfg, resources::filter_con);
t_filter.get_locations(locs, true);
// Loop through sides.
@ -484,7 +486,7 @@ namespace { // Support functions
// Filter the locations.
std::set<map_location> locs;
const terrain_filter filter(cfg, *resources::units);
const terrain_filter filter(cfg, resources::filter_con);
filter.get_locations(locs, true);
BOOST_FOREACH(const int &side_num, sides)
@ -777,8 +779,9 @@ WML_HANDLER_FUNCTION(heal_unit, event_info, cfg)
const vconfig & healers_filter = cfg.child("filter_second");
std::vector<unit*> healers;
if (!healers_filter.null()) {
const unit_filter ufilt(healers_filter, resources::filter_con);
BOOST_FOREACH(unit& u, *units) {
if ( u.matches_filter(healers_filter) && u.has_ability_type("heals") ) {
if ( ufilt(u) && u.has_ability_type("heals") ) {
healers.push_back(&u);
}
}
@ -793,13 +796,15 @@ WML_HANDLER_FUNCTION(heal_unit, event_info, cfg)
const vconfig & healed_filter = cfg.child("filter");
bool only_unit_at_loc1 = healed_filter.null();
bool heal_amount_to_set = true;
const unit_filter ufilt(healed_filter, resources::filter_con);
for(unit_map::unit_iterator u = units->begin(); u != units->end(); ++u) {
if (only_unit_at_loc1)
{
u = units->find(event_info.loc1);
if(!u.valid()) return;
}
else if ( !u->matches_filter(healed_filter) ) continue;
else if ( !ufilt(*u) ) continue;
int heal_amount = u->max_hitpoints() - u->hitpoints();
if(amount.blank() || amount == "full") u->set_hitpoints(u->max_hitpoints());
@ -856,9 +861,10 @@ WML_HANDLER_FUNCTION(kill, event_info, cfg)
if(cfg["fire_event"].to_bool() && secondary_unit)
{
secondary_unit = false;
const unit_filter ufilt(cfg.child("secondary_unit"), resources::filter_con);
for(unit_map::const_unit_iterator unit = resources::units->begin();
unit != resources::units->end(); ++unit) {
if ( unit->matches_filter(cfg.child("secondary_unit")) )
if ( ufilt( *unit) )
{
killer_loc = entity_location(*unit);
secondary_unit = true;
@ -872,8 +878,9 @@ 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;
const unit_filter ufilt(cfg, resources::filter_con);
BOOST_FOREACH(unit & u, *resources::units){
if ( u.matches_filter(cfg) ) {
if ( ufilt(u) ) {
dead_men_walking.push_back(&u);
}
}
@ -941,13 +948,14 @@ WML_HANDLER_FUNCTION(kill, event_info, cfg)
if((cfg_x.empty() || cfg_x == "recall")
&& (cfg_y.empty() || cfg_y == "recall"))
{
const unit_filter ufilt(cfg, resources::filter_con);
//remove the unit from the corresponding team's recall list
for(std::vector<team>::iterator pi = resources::teams->begin();
pi!=resources::teams->end(); ++pi)
{
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 (ufilt( *(*j), map_location() )) {
j = pi->recall_list().erase(j);
} else {
++j;
@ -1120,10 +1128,10 @@ WML_HANDLER_FUNCTION(modify_ai, /*event_info*/, cfg)
ERR_NG << "duplicate side information in [modify_ai]" << std::endl;
return;
}
side_filter ssf(filter_side);
side_filter ssf(filter_side, resources::filter_con);
sides = ssf.get_teams();
} else {
side_filter ssf(cfg);
side_filter ssf(cfg, resources::filter_con);
sides = ssf.get_teams();
}
BOOST_FOREACH(const int &side_num, sides)
@ -1414,8 +1422,9 @@ WML_HANDLER_FUNCTION(object, event_info, cfg)
map_location loc;
if(!filter.null()) {
const unit_filter ufilt(filter, resources::filter_con);
BOOST_FOREACH(const unit &u, *resources::units) {
if ( u.matches_filter(filter) ) {
if ( ufilt(u) ) {
loc = u.get_location();
break;
}
@ -1430,7 +1439,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(filter, resources::filter_con).matches(*u)) )
{
///@deprecated This can be removed (and a proper duration=level implemented) after 1.11.2
/// Don't forget to remove it from wmllint too!
@ -1526,7 +1535,7 @@ WML_HANDLER_FUNCTION(recall, /*event_info*/, cfg)
*/
temp_config["x"] = "recall";
temp_config["y"] = "recall";
vconfig unit_filter(temp_config);
vconfig unit_filter_cfg(temp_config);
const vconfig & leader_filter = cfg.child("secondary_unit");
for(int index = 0; index < int(resources::teams->size()); ++index) {
@ -1542,10 +1551,12 @@ WML_HANDLER_FUNCTION(recall, /*event_info*/, cfg)
recall_list_manager & avail = (*resources::teams)[index].recall_list();
std::vector<unit_map::unit_iterator> leaders = resources::units->find_leaders(index + 1);
const unit_filter ufilt(unit_filter_cfg, resources::filter_con);
const unit_filter lfilt(leader_filter, resources::filter_con); // Note that if leader_filter is null, this correctly gives a null filter that matches all units.
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 (ufilt(*(*u), map_location())) {
DBG_NG << (*u)->id() << " matched the filter...\n";
const unit_ptr to_recruit = *u;
const unit* pass_check = to_recruit.get();
@ -1556,8 +1567,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 ( lfilt(*leader) &&
unit_filter(vconfig(leader->recall_filter()), resources::filter_con).matches( *(*u),map_location() ) ) {
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();
@ -1606,7 +1617,7 @@ WML_HANDLER_FUNCTION(redraw, /*event_info*/, cfg)
}
if (clear_shroud_bool) {
side_filter filter(cfg);
side_filter filter(cfg, resources::filter_con);
BOOST_FOREACH(const int side, filter.get_teams()){
actions::clear_shroud(side);
}
@ -1719,13 +1730,13 @@ WML_HANDLER_FUNCTION(role, /*event_info*/, cfg)
std::vector<std::string>::iterator ti = types.begin(),
ti_end = types.end();
// loop to give precedence based on type order
const unit_filter ufilt(filter, resources::filter_con);
do {
if (has_any_types) {
item["type"] = *ti;
}
unit_map::iterator itor;
BOOST_FOREACH(unit &u, *resources::units) {
if ( u.matches_filter(filter) ) {
if ( ufilt(u) ) {
u.set_role(cfg["role"]);
found = true;
break;
@ -1745,6 +1756,7 @@ WML_HANDLER_FUNCTION(role, /*event_info*/, cfg)
}
// loop to give precedence based on type order
std::vector<std::string>::iterator ti = types.begin();
const unit_filter ufilt(filter, resources::filter_con);
do {
if (has_any_types) {
item["type"] = *ti;
@ -1762,7 +1774,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 (ufilt( *u, map_location() )) {
u->set_role(cfg["role"]);
found=true;
break;
@ -2285,8 +2297,9 @@ WML_HANDLER_FUNCTION(teleport, event_info, cfg)
// Search for a valid unit filter, and if we have one, look for the matching unit
const vconfig & filter = cfg.child("filter");
if(!filter.null()) {
const unit_filter ufilt(filter, resources::filter_con);
for (u = resources::units->begin(); u != resources::units->end(); ++u){
if ( u->matches_filter(filter) )
if ( ufilt(*u) )
break;
}
}
@ -2397,7 +2410,7 @@ WML_HANDLER_FUNCTION(time_area, /*event_info*/, cfg)
id = ids;
}
std::set<map_location> locs;
const terrain_filter filter(cfg, *resources::units);
const terrain_filter filter(cfg, resources::filter_con);
filter.get_locations(locs, true);
config parsed_cfg = cfg.get_parsed_config();
resources::tod_manager->add_time_area(id, locs, parsed_cfg);

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"
@ -72,9 +74,10 @@ namespace { // Support functions
std::vector<std::pair<int,int> > counts = (*u).has_attribute("count")
? utils::parse_ranges((*u)["count"]) : default_counts;
int match_count = 0;
const unit_filter ufilt(*u, resources::filter_con);
BOOST_FOREACH(const unit &i, *resources::units)
{
if ( i.hitpoints() > 0 && i.matches_filter(*u) ) {
if ( i.hitpoints() > 0 && ufilt(i) ) {
++match_count;
if(counts == default_counts) {
// by default a single match is enough, so avoid extra work
@ -84,6 +87,7 @@ namespace { // Support functions
}
if ((*u)["search_recall_list"].to_bool())
{
const unit_filter ufilt(*u, resources::filter_con);
for(std::vector<team>::iterator team = resources::teams->begin();
team!=resources::teams->end(); ++team)
{
@ -95,7 +99,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 ( ufilt( *team->recall_list()[t] ) ) {
++match_count;
}
}
@ -112,7 +116,7 @@ namespace { // Support functions
backwards_compat = backwards_compat && have_location.empty();
for(vconfig::child_list::const_iterator v = have_location.begin(); v != have_location.end(); ++v) {
std::set<map_location> res;
terrain_filter(*v, *resources::units).get_locations(res);
terrain_filter(*v, resources::filter_con).get_locations(res);
std::vector<std::pair<int,int> > counts = (*v).has_attribute("count")
? utils::parse_ranges((*v)["count"]) : default_counts;

View file

@ -20,7 +20,9 @@
#include "global.hpp"
#include "entity_location.hpp"
#include "../resources.hpp"
#include "../unit.hpp"
#include "../unit_filter.hpp"
#include "../variable.hpp"
@ -98,7 +100,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(filter, resources::filter_con).matches(*un_it, filter_loc_) &&
matches_unit(un_it);
}

View file

@ -171,7 +171,7 @@ bool wml_menu_item::can_show(const map_location & hex) const
// Failing the [fiter_location] tag means no show.
if ( !filter_location_.empty() &&
!terrain_filter(filter_location_, *resources::units)(hex) )
!terrain_filter(filter_location_, resources::filter_con)(hex) )
return false;
// Failing to have a required selection means no show.

View file

@ -170,7 +170,7 @@ namespace { // Support functions
BOOST_FOREACH(const vconfig &f, filters.get_children("filter_side"))
{
side_filter ssf(f);
side_filter ssf(f, resources::filter_con);
if ( !ssf.match(resources::controller->current_side()) )
return false;
}

View file

@ -17,6 +17,7 @@
class config;
#include "filter_context.hpp"
#include "game_board.hpp"
#include "game_data.hpp"
#include "tod_manager.hpp"
@ -25,7 +26,9 @@ class config;
namespace pathfind { class manager; }
struct game_state {
class game_state : public filter_context
{
public:
const config& level_;
game_data gamedata_;
game_board board_;
@ -43,6 +46,9 @@ struct game_state {
void init(int ticks, const config & replay_start);
config to_config() const;
virtual const display_context & get_disp_context() const { return board_; }
virtual const tod_manager & get_tod_man() const { return tod_manager_; }
};
#endif

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,14 +76,15 @@ 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)) {
const unit_filter ufilt(filter, resources::filter_con);
if (ufilt.matches(u, loc)) {
scoped_xy_unit teleport_unit("teleport_unit", loc.x, loc.y, *resources::units);
terrain_filter source_filter(source, *units);
terrain_filter source_filter(source, resources::filter_con);
source_filter.get_locations(reversed_ ? loc_pair.second : loc_pair.first);
terrain_filter target_filter(target, *units);
terrain_filter target_filter(target, resources::filter_con);
target_filter.get_locations(reversed_ ? loc_pair.first : loc_pair.second);
}
}

View file

@ -79,6 +79,7 @@ static lg::log_domain log_engine_enemies("engine/enemies");
static void clear_resources()
{
resources::controller = NULL;
resources::filter_con = NULL;
resources::gameboard = NULL;
resources::gamedata = NULL;
resources::persist = NULL;
@ -145,7 +146,7 @@ play_controller::play_controller(const config& level, saved_game& state_of_game,
resources::tod_manager = &gamestate_.tod_manager_;
resources::undo_stack = undo_stack_.get();
resources::units = &gamestate_.board_.units_;
resources::filter_con = &gamestate_;
resources::classification = &saved_game_.classification();
resources::mp_settings = &saved_game_.mp_settings();

View file

@ -67,7 +67,7 @@ namespace wb {
} // namespace wb
// Holds gamestate related objects
struct game_state;
class game_state;
class play_controller : public controller_base, public events::observer, public savegame::savegame_config
{

View file

@ -21,6 +21,7 @@ namespace resources
game_config_manager *config_manager = NULL;
play_controller *controller = NULL;
game_data *gamedata = NULL;
filter_context *filter_con = NULL;
LuaKernel *lua_kernel = NULL;
persist_manager *persist = NULL;
game_display *screen = NULL;

View file

@ -23,6 +23,7 @@ class game_config_manager;
class game_display;
class gamemap;
class game_data;
class filter_context;
class LuaKernel;
class play_controller;
class team;
@ -52,6 +53,7 @@ namespace resources
extern persist_manager *persist;
extern game_classification *classification;
extern game_display *screen;
extern filter_context *filter_con;
extern const mp_game_settings *mp_settings;
extern soundsource::manager *soundsources;
extern std::vector<team> *teams;

View file

@ -41,6 +41,7 @@
#include "config.hpp" // for config, etc
#include "display_chat_manager.hpp" // for clear_chat_messages
#include "filesystem.hpp" // for get_wml_location
#include "filter_context.hpp"
#include "font.hpp" // for LABEL_COLOR
#include "game_board.hpp" // for game_board
#include "game_classification.hpp" // for game_classification, etc
@ -98,6 +99,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
@ -854,10 +856,11 @@ static int intf_get_units(lua_State *L)
lua_newtable(L);
int i = 1;
unit_map &units = *resources::units;
const unit_filter ufilt(filter, resources::filter_con); // note that if filter is null, this yields a null filter matching everything
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 (!ufilt(*ui))
continue;
new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(ui->underlying_id());
lua_pushvalue(L, 1);
@ -894,11 +897,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(filter, resources::filter_con).matches(*u, map_location()));
return 1;
}
lua_pushboolean(L, u->matches_filter(filter, u->get_location()));
lua_pushboolean(L, unit_filter(filter, resources::filter_con).matches(*u));
return 1;
}
@ -920,6 +923,7 @@ static int intf_get_recall_units(lua_State *L)
lua_rawget(L, LUA_REGISTRYINDEX);
lua_newtable(L);
int i = 1, s = 1;
const unit_filter ufilt(filter, resources::filter_con);
BOOST_FOREACH(team &t, *resources::teams)
{
BOOST_FOREACH(unit_ptr & u, t.recall_list())
@ -927,7 +931,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 (!ufilt( *u, map_location() ))
continue;
}
new(lua_newuserdata(L, sizeof(lua_unit))) lua_unit(s, u->underlying_id());
@ -1998,11 +2002,12 @@ static int intf_find_cost_map(lua_State *L)
else if (!filter.null()) // 1. arg - filter
{
unit_map &units = *resources::units;
const unit_filter ufilt(filter, resources::filter_con);
for (unit_map::const_unit_iterator ui = units.begin(), ui_end = units.end();
ui != ui_end; ++ui)
{
bool on_map = ui->get_location().valid();
if (on_map && ui->matches_filter(filter, ui->get_location()))
if (on_map && ufilt(*ui))
{
real_units. push_back(&(*ui));
}
@ -2118,7 +2123,7 @@ static int intf_find_cost_map(lua_State *L)
{
filter = vconfig(config(), true);
}
const terrain_filter t_filter(filter, *resources::units);
const terrain_filter t_filter(filter, resources::filter_con);
t_filter.get_locations(location_set, true);
++arg;
@ -3162,7 +3167,7 @@ static int intf_get_locations(lua_State *L)
vconfig filter = luaW_checkvconfig(L, 1);
std::set<map_location> res;
const terrain_filter t_filter(filter, *resources::units);
const terrain_filter t_filter(filter, resources::filter_con);
t_filter.get_locations(res, true);
lua_createtable(L, res.size(), 0);
@ -3194,7 +3199,7 @@ static int intf_get_villages(lua_State *L)
vconfig filter = luaW_checkvconfig(L, 1);
for(std::vector<map_location>::const_iterator it = locs.begin(); it != locs.end(); ++it) {
bool matches = terrain_filter(filter, *resources::units).match(*it);
bool matches = terrain_filter(filter, resources::filter_con).match(*it);
if (matches) {
lua_createtable(L, 2, 0);
lua_pushinteger(L, it->x + 1);
@ -3225,7 +3230,7 @@ static int intf_match_location(lua_State *L)
return 1;
}
const terrain_filter t_filter(filter, *resources::units);
const terrain_filter t_filter(filter, resources::filter_con);
lua_pushboolean(L, t_filter.match(map_location(x, y)));
return 1;
}
@ -3249,7 +3254,7 @@ static int intf_match_side(lua_State *L)
return 1;
}
side_filter s_filter(filter);
side_filter s_filter(filter, resources::filter_con);
lua_pushboolean(L, s_filter.match(side + 1));
return 1;
}
@ -3267,7 +3272,7 @@ static int intf_get_sides(lua_State* L)
for(unsigned side_number = 1; side_number <= resources::teams->size(); ++side_number)
sides.push_back(side_number);
} else {
side_filter filter(ssf);
side_filter filter(ssf, resources::filter_con);
sides = filter.get_teams();
}

View file

@ -17,15 +17,17 @@
#include "global.hpp"
#include "config.hpp"
#include "display_context.hpp"
#include "filter_context.hpp"
#include "log.hpp"
#include "recall_list_manager.hpp"
#include "resources.hpp"
#include "side_filter.hpp"
#include "variable.hpp"
#include "team.hpp"
#include "serialization/string_utils.hpp"
#include "network.hpp"
#include "unit.hpp"
#include "unit_filter.hpp"
#include "unit_map.hpp"
#include <boost/foreach.hpp>
@ -38,10 +40,11 @@ static lg::log_domain log_engine_sf("engine/side_filter");
// and so we don't care about the warnings this quick fix generates
#pragma warning(push)
#pragma warning(disable:4413)
side_filter::side_filter():
cfg_(vconfig::unconstructed_vconfig()),
flat_(),
side_string_()
side_filter::side_filter()
: cfg_(vconfig::unconstructed_vconfig())
, flat_()
, side_string_()
, fc_(NULL)
{
assert(false);
}
@ -49,23 +52,25 @@ side_filter::side_filter():
#endif
side_filter::side_filter(const vconfig& cfg, bool flat_tod) :
cfg_(cfg),
flat_(flat_tod),
side_string_()
side_filter::side_filter(const vconfig& cfg, const filter_context * fc, bool flat_tod)
: cfg_(cfg)
, flat_(flat_tod)
, side_string_()
, fc_(fc)
{
}
side_filter::side_filter(const std::string &side_string, bool flat_tod)
: cfg_(vconfig::empty_vconfig()), flat_(flat_tod), side_string_(side_string)
side_filter::side_filter(const std::string &side_string, const filter_context * fc, bool flat_tod)
: cfg_(vconfig::empty_vconfig()), flat_(flat_tod), side_string_(side_string), fc_(fc)
{
}
std::vector<int> side_filter::get_teams() const
{
assert(fc_);
//@todo: replace with better implementation
std::vector<int> result;
BOOST_FOREACH(const team &t, *resources::teams) {
BOOST_FOREACH(const team &t, fc_->get_disp_context().teams()) {
if (match(t)) {
result.push_back(t.side());
}
@ -89,6 +94,8 @@ static bool check_side_number(const team &t, const std::string &str)
bool side_filter::match_internal(const team &t) const
{
assert(fc_);
if (cfg_.has_attribute("side_in")) {
if (!check_side_number(t,cfg_["side_in"])) {
return false;
@ -128,21 +135,22 @@ bool side_filter::match_internal(const team &t) const
//Allow filtering on units
if(cfg_.has_child("has_unit")) {
const vconfig& unit_filter = cfg_.child("has_unit");
const vconfig & ufilt_cfg = cfg_.child("has_unit");
const unit_filter ufilt(ufilt_cfg, fc_, flat_);
bool found = false;
BOOST_FOREACH(unit &u, *resources::units) {
BOOST_FOREACH(const unit &u, fc_->get_disp_context().units()) {
if (u.side() != t.side()) {
continue;
}
if (u.matches_filter(unit_filter, u.get_location(), flat_)) {
if (ufilt(u)) {
found = true;
break;
}
}
if(!found && unit_filter["search_recall_list"].to_bool(false)) {
if(!found && ufilt_cfg["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(ufilt(*u)) {
found = true;
break;
}
@ -155,22 +163,22 @@ bool side_filter::match_internal(const team &t) const
const vconfig& enemy_of = cfg_.child("enemy_of");
if(!enemy_of.null()) {
side_filter s_filter(enemy_of);
side_filter s_filter(enemy_of, fc_);
const std::vector<int>& teams = s_filter.get_teams();
if(teams.empty()) return false;
BOOST_FOREACH(const int side, teams) {
if(!(*resources::teams)[side - 1].is_enemy(t.side()))
if(!(fc_->get_disp_context().teams())[side - 1].is_enemy(t.side()))
return false;
}
}
const vconfig& allied_with = cfg_.child("allied_with");
if(!allied_with.null()) {
side_filter s_filter(allied_with);
side_filter s_filter(allied_with, fc_);
const std::vector<int>& teams = s_filter.get_teams();
if(teams.empty()) return false;
BOOST_FOREACH(const int side, teams) {
if((*resources::teams)[side - 1].is_enemy(t.side()))
if((fc_->get_disp_context().teams())[side - 1].is_enemy(t.side()))
return false;
}
}
@ -192,7 +200,8 @@ bool side_filter::match_internal(const team &t) const
bool side_filter::match(int side) const
{
return this->match((*resources::teams)[side-1]);
assert(fc_);
return this->match((fc_->get_disp_context().teams())[side-1]);
}
bool side_filter::match(const team& t) const
@ -209,17 +218,17 @@ bool side_filter::match(const team& t) const
//handle [and]
if(cond_name == "and")
{
matches = matches && side_filter(cond_cfg, flat_).match(t);
matches = matches && side_filter(cond_cfg, fc_, flat_).match(t);
}
//handle [or]
else if(cond_name == "or")
{
matches = matches || side_filter(cond_cfg, flat_).match(t);
matches = matches || side_filter(cond_cfg, fc_, flat_).match(t);
}
//handle [not]
else if(cond_name == "not")
{
matches = matches && !side_filter(cond_cfg, flat_).match(t);
matches = matches && !side_filter(cond_cfg, fc_, flat_).match(t);
}
++cond;
}

View file

@ -18,6 +18,7 @@
#include "variable.hpp"
class config;
class filter_context;
class unit;
class unit_map;
class team;
@ -35,8 +36,8 @@ public:
side_filter();
#endif
side_filter(const std::string &side_string, bool flat_tod = false);
side_filter(const vconfig &cfg, bool flat_tod = false);
side_filter(const std::string &side_string, const filter_context * fc, bool flat_tod = false);
side_filter(const vconfig &cfg, const filter_context * fc, bool flat_tod = false);
//match: returns true if and only if the given team matches this filter
bool match(const team& t) const;
@ -54,6 +55,7 @@ private:
bool flat_;
std::string side_string_;
const filter_context * fc_; //!< The filter context for this filter. It should be a pointer because otherwise the default ctor doesn't work
};
#endif

View file

@ -17,15 +17,17 @@
#include "global.hpp"
#include "config.hpp"
#include "display_context.hpp"
#include "filter_context.hpp"
#include "game_board.hpp"
#include "log.hpp"
#include "map.hpp"
#include "resources.hpp"
#include "side_filter.hpp"
#include "team.hpp"
#include "terrain_filter.hpp"
#include "tod_manager.hpp"
#include "unit.hpp"
#include "unit_filter.hpp"
#include "variable.hpp"
#include <boost/foreach.hpp>
@ -45,7 +47,7 @@ terrain_filter::~terrain_filter()
#pragma warning(disable:4413)
terrain_filter::terrain_filter():
cfg_(vconfig::unconstructed_vconfig()),
units_(unit_map()),
fc_(NULL),
cache_(),
max_loop_(),
flat_()
@ -56,10 +58,10 @@ terrain_filter::terrain_filter():
#endif
terrain_filter::terrain_filter(const vconfig& cfg, const unit_map& units,
terrain_filter::terrain_filter(const vconfig& cfg, const filter_context * fc,
const bool flat_tod, const size_t max_loop) :
cfg_(cfg),
units_(units),
fc_(fc),
cache_(),
max_loop_(max_loop),
flat_(flat_tod)
@ -68,7 +70,7 @@ terrain_filter::terrain_filter(const vconfig& cfg, const unit_map& units,
terrain_filter::terrain_filter(const vconfig& cfg, const terrain_filter& original) :
cfg_(cfg),
units_(original.units_),
fc_(original.fc_),
cache_(),
max_loop_(original.max_loop_),
flat_(original.flat_)
@ -79,7 +81,7 @@ terrain_filter::terrain_filter(const terrain_filter& other) :
xy_pred(), // We should construct this too, since it has no datamembers
// use the default constructor.
cfg_(other.cfg_),
units_(other.units_),
fc_(other.fc_),
cache_(),
max_loop_(other.max_loop_),
flat_(other.flat_)
@ -108,7 +110,7 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
{
//Filter Areas
if (cfg_.has_attribute("area") &&
resources::tod_manager->get_area_by_id(cfg_["area"]).count(loc) == 0)
fc_->get_tod_man().get_area_by_id(cfg_["area"]).count(loc) == 0)
return false;
if(cfg_.has_attribute("terrain")) {
@ -116,7 +118,7 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
cache_.parsed_terrain = new t_translation::t_match(cfg_["terrain"]);
}
if(!cache_.parsed_terrain->is_empty) {
const t_translation::t_terrain letter = resources::gameboard->map().get_terrain_info(loc).number();
const t_translation::t_terrain letter = fc_->get_disp_context().map().get_terrain_info(loc).number();
if(!t_translation::terrain_matches(letter, *cache_.parsed_terrain)) {
return false;
}
@ -151,9 +153,9 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
//Allow filtering on unit
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_))
const unit_filter ufilt(vconfig(cfg_.child("filter")), fc_, flat_);
const unit_map::const_iterator u = fc_->get_disp_context().units().find(loc);
if (u == fc_->get_disp_context().units().end() || !ufilt( *u, loc))
return false;
}
@ -165,11 +167,11 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
bool visible = (*i)["visible"].to_bool(true);
bool respect_fog = (*i)["respect_fog"].to_bool(true);
side_filter ssf(*i);
side_filter ssf(*i, fc_);
std::vector<int> sides = ssf.get_teams();
BOOST_FOREACH(const int side, sides) {
const team &viewing_team = resources::teams->at(side - 1);
const team &viewing_team = fc_->get_disp_context().teams().at(side - 1);
bool viewer_sees = respect_fog ? !viewing_team.fogged(loc) : !viewing_team.shrouded(loc);
if (visible != viewer_sees) {
return false;
@ -192,7 +194,7 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
for (j = dirs.begin(); j != j_end; ++j) {
map_location &adj = adjacent[*j];
if (resources::gameboard->map().on_board(adj)) {
if (fc_->get_disp_context().map().on_board(adj)) {
if(cache_.adjacent_matches == NULL) {
while(index >= std::distance(cache_.adjacent_match_cache.begin(), cache_.adjacent_match_cache.end())) {
const vconfig& adj_cfg = adj_cfgs[cache_.adjacent_match_cache.size()];
@ -241,9 +243,9 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
time_of_day tod;
if(flat_) {
tod = resources::tod_manager->get_time_of_day(loc);
tod = fc_->get_tod_man().get_time_of_day(loc);
} else {
tod = resources::tod_manager->get_illuminated_time_of_day(resources::gameboard->units(), resources::gameboard->map(),loc);
tod = fc_->get_tod_man().get_illuminated_time_of_day(fc_->get_disp_context().units(), fc_->get_disp_context().map(),loc);
}
if(!tod_type.empty()) {
@ -284,15 +286,15 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
if(!owner_side.empty()) {
WRN_NG << "duplicate side information in a SLF, ignoring inline owner_side=" << std::endl;
}
if(!resources::gameboard->map().is_village(loc))
if(!fc_->get_disp_context().map().is_village(loc))
return false;
side_filter ssf(filter_owner);
side_filter ssf(filter_owner, fc_);
const std::vector<int>& sides = ssf.get_teams();
bool found = false;
if(sides.empty() && resources::gameboard->village_owner(loc) == -1)
if(sides.empty() && fc_->get_disp_context().village_owner(loc) == -1)
found = true;
BOOST_FOREACH(const int side, sides) {
if(resources::teams->at(side - 1).owns_village(loc)) {
if(fc_->get_disp_context().teams().at(side - 1).owns_village(loc)) {
found = true;
break;
}
@ -302,7 +304,7 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
}
else if(!owner_side.empty()) {
const int side_index = owner_side.to_int(0) - 1;
if(resources::gameboard->village_owner(loc) != side_index) {
if(fc_->get_disp_context().village_owner(loc) != side_index) {
return false;
}
}
@ -313,7 +315,7 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
bool terrain_filter::match(const map_location& loc) const
{
if(cfg_["x"] == "recall" && cfg_["y"] == "recall") {
return !resources::gameboard->map().on_board(loc);
return !fc_->get_disp_context().map().on_board(loc);
}
std::set<map_location> hexes;
std::vector<map_location> loc_vec(1, loc);
@ -329,9 +331,9 @@ bool terrain_filter::match(const map_location& loc) const
hexes.insert(loc_vec.begin(), loc_vec.end());
else if ( cfg_.has_child("filter_radius") ) {
terrain_filter r_filter(cfg_.child("filter_radius"), *this);
get_tiles_radius(resources::gameboard->map(), loc_vec, radius, hexes, false, r_filter);
get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes, false, r_filter);
} else {
get_tiles_radius(resources::gameboard->map(), loc_vec, radius, hexes);
get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes);
}
size_t loop_count = 0;
@ -389,9 +391,9 @@ void terrain_filter::get_locations(std::set<map_location>& locs, bool with_borde
&& !cfg_.has_attribute("area") ) {
//consider all locations on the map
int bs = resources::gameboard->map().border_size();
int w = with_border ? resources::gameboard->map().w() + bs : resources::gameboard->map().w();
int h = with_border ? resources::gameboard->map().h() + bs : resources::gameboard->map().h();
int bs = fc_->get_disp_context().map().border_size();
int w = with_border ? fc_->get_disp_context().map().w() + bs : fc_->get_disp_context().map().w();
int h = with_border ? fc_->get_disp_context().map().h() + bs : fc_->get_disp_context().map().h();
for (int x = with_border ? 0 - bs : 0; x < w; ++x) {
for (int y = with_border ? 0 - bs : 0; y < h; ++y) {
match_set.insert(map_location(x,y));
@ -405,7 +407,7 @@ void terrain_filter::get_locations(std::set<map_location>& locs, bool with_borde
&& !cfg_.has_attribute("area") ) {
std::vector<map_location> xy_vector;
xy_vector = resources::gameboard->map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
xy_vector = fc_->get_disp_context().map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
match_set.insert(xy_vector.begin(), xy_vector.end());
} else
@ -434,7 +436,7 @@ void terrain_filter::get_locations(std::set<map_location>& locs, bool with_borde
&& !cfg_.has_attribute("find_in")
&& cfg_.has_attribute("area") ) {
const std::set<map_location>& area = resources::tod_manager->get_area_by_id(cfg_["area"]);
const std::set<map_location>& area = fc_->get_tod_man().get_area_by_id(cfg_["area"]);
match_set.insert(area.begin(), area.end());
} else
@ -444,7 +446,7 @@ void terrain_filter::get_locations(std::set<map_location>& locs, bool with_borde
&& !cfg_.has_attribute("area") ) {
std::vector<map_location> xy_vector;
xy_vector = resources::gameboard->map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
xy_vector = fc_->get_disp_context().map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
match_set.insert(xy_vector.begin(), xy_vector.end());
// remove any locations not found in the specified variable
@ -477,8 +479,8 @@ void terrain_filter::get_locations(std::set<map_location>& locs, bool with_borde
&& cfg_.has_attribute("area") ) {
std::vector<map_location> xy_vector;
xy_vector = resources::gameboard->map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
const std::set<map_location>& area = resources::tod_manager->get_area_by_id(cfg_["area"]);
xy_vector = fc_->get_disp_context().map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
const std::set<map_location>& area = fc_->get_tod_man().get_area_by_id(cfg_["area"]);
BOOST_FOREACH(const map_location& loc, xy_vector) {
if (area.count(loc) != 0)
@ -491,7 +493,7 @@ void terrain_filter::get_locations(std::set<map_location>& locs, bool with_borde
&& cfg_.has_attribute("find_in")
&& cfg_.has_attribute("area") ) {
const std::set<map_location>& area = resources::tod_manager->get_area_by_id(cfg_["area"]);
const std::set<map_location>& area = fc_->get_tod_man().get_area_by_id(cfg_["area"]);
//use content of find_in as starting set
variable_info vi(cfg_["find_in"], false, variable_info::TYPE_CONTAINER);
@ -516,10 +518,10 @@ void terrain_filter::get_locations(std::set<map_location>& locs, bool with_borde
&& cfg_.has_attribute("area") ) {
const std::vector<map_location>& xy_vector =
resources::gameboard->map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
fc_->get_disp_context().map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
std::set<map_location> xy_set(xy_vector.begin(), xy_vector.end());
const std::set<map_location>& area = resources::tod_manager->get_area_by_id(cfg_["area"]);
const std::set<map_location>& area = fc_->get_tod_man().get_area_by_id(cfg_["area"]);
//use content of find_in as starting set
variable_info vi(cfg_["find_in"], false, variable_info::TYPE_CONTAINER);
@ -629,9 +631,9 @@ void terrain_filter::get_locations(std::set<map_location>& locs, bool with_borde
std::vector<map_location> xy_vector (match_set.begin(), match_set.end());
if(cfg_.has_child("filter_radius")) {
terrain_filter r_filter(cfg_.child("filter_radius"), *this);
get_tiles_radius(resources::gameboard->map(), xy_vector, radius, locs, with_border, r_filter);
get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border, r_filter);
} else {
get_tiles_radius(resources::gameboard->map(), xy_vector, radius, locs, with_border);
get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border);
}
} else {
locs.insert(match_set.begin(), match_set.end());

View file

@ -21,6 +21,7 @@
#include "variable.hpp"
class config;
class filter_context;
class unit;
class unit_map;
class team;
@ -37,7 +38,7 @@ public:
#endif
terrain_filter(const vconfig& cfg,
const unit_map& units, const bool flat_tod=false, const size_t max_loop=game_config::max_loop);
const filter_context * fc, const bool flat_tod=false, const size_t max_loop=game_config::max_loop);
terrain_filter(const vconfig& cfg, const terrain_filter& original);
/** Default implementation, but defined out-of-line for efficiency reasons. */
~terrain_filter();
@ -66,7 +67,7 @@ private:
bool match_internal(const map_location& loc, const bool ignore_xy) const;
const vconfig cfg_; //config contains WML for a Standard Location Filter
const unit_map& units_;
const filter_context * fc_;
struct terrain_filter_cache {
terrain_filter_cache() :

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(vconfig(afilter), resources::filter_con).matches(*this, loc_)) 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(vconfig(afilter), resources::filter_con, illuminates).matches(*this, loc) )
return false;
map_location adjacent[6];
@ -304,6 +306,7 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
BOOST_FOREACH(const config &i, cfg.child_range("filter_adjacent"))
{
const unit_filter ufilt(vconfig(i), resources::filter_con, illuminates);
BOOST_FOREACH(const std::string &j, utils::split(i["adjacent"]))
{
map_location::DIRECTION index =
@ -313,21 +316,22 @@ 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 (!ufilt( *unit ))
return false;
}
}
BOOST_FOREACH(const config &i, cfg.child_range("filter_adjacent_location"))
{
terrain_filter adj_filter(vconfig(i), resources::filter_con);
adj_filter.flatten(illuminates);
BOOST_FOREACH(const std::string &j, utils::split(i["adjacent"]))
{
map_location::DIRECTION index = map_location::parse_direction(j);
if (index == map_location::NDIRECTIONS) {
continue;
}
terrain_filter adj_filter(vconfig(i), units);
adj_filter.flatten(illuminates);
if(!adj_filter.match(adjacent[index])) {
return false;
}
@ -346,12 +350,13 @@ bool unit::ability_affects_adjacent(const std::string& ability, const config& cf
assert(dir >=0 && dir <= 5);
static const std::string adjacent_names[6] = {"n","ne","se","s","sw","nw"};
BOOST_FOREACH(const config &i, cfg.child_range("affect_adjacent"))
{
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(vconfig(filter), resources::filter_con, illuminates).matches(*this, loc) )
return true;
} else
return true;
@ -369,7 +374,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(vconfig(filter), resources::filter_con, ability == "illuminates").matches(*this, loc);
}
bool unit::has_ability_type(const std::string& ability) const
@ -792,7 +797,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(vconfig(filter_child), resources::filter_con).matches(*un_it, loc) )
return false;
// Check for a weapon match.
@ -882,7 +887,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(vconfig(i), resources::filter_con).matches(*unit, adjacent[index]) ) //TODO: Should this filter get precomputed?
return false;
}
}
@ -896,7 +901,7 @@ bool attack_type::special_active(const config& special, AFFECTS whom,
map_location::parse_direction(j);
if (index == map_location::NDIRECTIONS)
continue;
terrain_filter adj_filter(vconfig(i), units);
terrain_filter adj_filter(vconfig(i), resources::filter_con);
if(!adj_filter.match(adjacent[index])) {
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(vconfig(*myitor), &disp).matches(*my_unit, loc)) return MATCH_FAIL; //TODO: Precalculate these
++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(vconfig(*second_itor), &disp).matches(*unit, second_loc)) return MATCH_FAIL; //TODO: Precalculate these
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>
@ -772,8 +773,9 @@ void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const m
// and if we have one, look for the matching unit
vconfig filter = cfg.child("filter");
if(!filter.null()) {
const unit_filter ufilt(filter, resources::filter_con);
for (u = resources::units->begin(); u != resources::units->end(); ++u) {
if ( u->matches_filter(filter) )
if ( ufilt(*u) )
break;
}
}
@ -826,7 +828,7 @@ void wml_animation_internal(unit_animator &animator, const vconfig &cfg, const m
vconfig t_filter = cfg.child("facing");
map_location secondary_loc = map_location::null_location();
if(!t_filter.empty()) {
terrain_filter filter(t_filter, *resources::units);
terrain_filter filter(t_filter, resources::filter_con);
std::set<map_location> locs;
filter.get_locations(locs);
if (!locs.empty() && u->get_location() != *locs.begin()) {

515
src/unit_filter.cpp Normal file
View file

@ -0,0 +1,515 @@
/*
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 "filter_context.hpp"
#include "make_enum.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>
#include <boost/ptr_container/ptr_vector.hpp>
#include <vector>
///Defined out of line to prevent including unit at unit_filter.hpp
bool unit_filter::matches(const unit & u) const {
return matches (u, u.get_location());
}
//bool unit_filter::matches(const unit & /*u*/, const map_location & /*loc*/) const {
// assert(false && "called match against a pure abstract unit_filter! this indicates a programmer error, this function must be overrided");
// return false;
//}
/// Null unit filter is built when the input config is null
class null_unit_filter_impl : public unit_filter_abstract_impl {
public:
null_unit_filter_impl() {}
virtual bool matches(const unit & /*u*/, const map_location & /*loc*/) const {
return true;
}
~null_unit_filter_impl() {}
};
/// This enum helps to evaluate conditional filters
namespace conditional {
MAKE_ENUM (TYPE,
(AND, "and")
(OR, "or")
(NOT, "not")
)
MAKE_ENUM_STREAM_OPS1(TYPE)
static TYPE warning_suppressor = string_to_TYPE_default("foo", NOT);
}
/// The basic unit filter gives a generic implementation of the match fcn
class basic_unit_filter_impl : public unit_filter_abstract_impl {
public:
basic_unit_filter_impl(const vconfig & vcfg, const filter_context & fc, bool flat_tod)
: vcfg_(vcfg)
, fc_(fc)
, use_flat_tod_(flat_tod)
{
// Handle [and], [or], and [not] with in-order precedence
vconfig::all_children_iterator cond = vcfg_.ordered_begin();
vconfig::all_children_iterator cond_end = vcfg_.ordered_end();
while(cond != cond_end)
{
try {
const std::string& cond_name = cond.get_key();
conditional::TYPE type = conditional::string_to_TYPE(cond_name); // throws bad_enum_cast if we don't get a string match with any enum
const vconfig& cond_filter = cond.get_child();
cond_children_.push_back(new basic_unit_filter_impl(cond_filter, fc_, use_flat_tod_));
cond_child_types_.push_back(type);
} catch (bad_enum_cast &) {} //ignore tags that aren't conditionals
++cond;
}
}
virtual bool matches(const unit & u, const map_location & loc) const;
~basic_unit_filter_impl() {}
private:
const vconfig vcfg_;
const filter_context & fc_;
bool use_flat_tod_;
boost::ptr_vector<unit_filter_abstract_impl> cond_children_;
std::vector<conditional::TYPE> cond_child_types_;
bool internal_matches_filter(const unit & u, const map_location & loc) const;
};
/** Ctor of unit filter
* unit_filter::unit_filter acts as a factory, selecting the appropriate implementation class
*/
unit_filter::unit_filter(const vconfig & vcfg, const filter_context * fc, bool flat_tod)
{
if (!fc) {
assert(false && "attempt to instantiate a unit filter with a null filter context!");
}
if (vcfg.null()) {
impl_.reset(new null_unit_filter_impl());
}
impl_.reset(new basic_unit_filter_impl(vcfg, *fc, flat_tod));
//TODO: Add more efficient implementations for special cases
}
/** Begin implementations of filter impl's
*/
bool basic_unit_filter_impl::matches(const unit & u, const map_location& loc) const
{
bool matches = true;
if(loc.valid()) {
scoped_xy_unit auto_store("this_unit", loc.x, loc.y, fc_.get_disp_context().units());
matches = internal_matches_filter(u, loc);
} else {
// If loc is invalid, then this is a recall list unit (already been scoped)
matches = internal_matches_filter(u, loc);
}
// Handle [and], [or], and [not] with in-order precedence
for (size_t i = 0; i < cond_children_.size(); i++) {
switch (cond_child_types_[i]) {
case conditional::AND:
matches = matches && cond_children_[i].matches(u,loc);
break;
case conditional::OR:
matches = matches || cond_children_[i].matches(u,loc);
break;
case conditional::NOT:
matches = matches && !cond_children_[i].matches(u,loc);
}
}
return matches;
}
bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_location& loc) const
{
config::attribute_value cfg_name = vcfg_["name"];
if (!cfg_name.blank() && cfg_name.str() != u.name()) {
return false;
}
const config::attribute_value cfg_id = vcfg_["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 = vcfg_["speaker"];
if (!cfg_speaker.blank() && cfg_speaker.str() != u.id()) {
return false;
}
if(vcfg_.has_child("filter_location")) {
const vconfig& t_cfg = vcfg_.child("filter_location");
terrain_filter t_filter(t_cfg, &fc_, use_flat_tod_);
if(!t_filter.match(loc)) {
return false;
}
}
const vconfig& filter_side = vcfg_.child("filter_side");
if(!filter_side.null()) {
side_filter s_filter(filter_side, &fc_);
if(!s_filter.match(u.side()))
return false;
}
// Also allow filtering on location ranges outside of the location filter
config::attribute_value cfg_x = vcfg_["x"];
config::attribute_value cfg_y = vcfg_["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 (fc_.get_disp_context().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 = vcfg_["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 = vcfg_["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 = vcfg_["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 = vcfg_["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 = vcfg_["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 = vcfg_["gender"];
if (!cfg_gender.blank() && string_gender(cfg_gender) != u.gender()) {
return false;
}
config::attribute_value cfg_side = vcfg_["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 = vcfg_["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 = vcfg_["role"];
if (!cfg_role.blank() && cfg_role.str() != u.get_role()) {
return false;
}
config::attribute_value cfg_ai_special = vcfg_["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 = vcfg_["canrecruit"];
if (!cfg_canrecruit.blank() && cfg_canrecruit.to_bool() != u.can_recruit()) {
return false;
}
config::attribute_value cfg_recall_cost = vcfg_["recall_cost"];
if (!cfg_recall_cost.blank() && cfg_recall_cost.to_int(-1) != u.recall_cost()) {
return false;
}
config::attribute_value cfg_level = vcfg_["level"];
if (!cfg_level.blank() && cfg_level.to_int(-1) != u.level()) {
return false;
}
config::attribute_value cfg_defense = vcfg_["defense"];
if (!cfg_defense.blank() && cfg_defense.to_int(-1) != u.defense_modifier(fc_.get_disp_context().map().get_terrain(loc))) {
return false;
}
config::attribute_value cfg_movement = vcfg_["movement_cost"];
if (!cfg_movement.blank() && cfg_movement.to_int(-1) != u.movement_cost(fc_.get_disp_context().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 = vcfg_.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 (vcfg_.has_child("filter_vision")) {
const vconfig::child_list& vis_filt = vcfg_.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, &fc_);
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 = fc_.get_disp_context().teams()[*viewer - 1].fogged(loc);
bool hiding = u.invisible(loc/*, false(?) */);
bool unit_hidden = fogged || hiding;
if (visible == unit_hidden) return false;
}
}
}
if (vcfg_.has_child("filter_adjacent")) {
const unit_map& units = fc_.get_disp_context().units();
map_location adjacent[6];
get_adjacent_tiles(loc, adjacent);
vconfig::child_list::const_iterator i, i_end;
const vconfig::child_list& adj_filt = vcfg_.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(*i, &fc_, use_flat_tod_).matches(*unit_itor)) {
continue;
}
config::attribute_value i_is_enemy = (*i)["is_enemy"];
if (i_is_enemy.blank() || i_is_enemy.to_bool() ==
fc_.get_disp_context().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 = vcfg_["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 = vcfg_["formula"];
if (!cfg_formula.blank()) {
if (!u.formula_manager().matches_filter(cfg_formula, loc, u)) {
return false;
}
}
config::attribute_value cfg_lua_function = vcfg_["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;
}

59
src/unit_filter.hpp Normal file
View file

@ -0,0 +1,59 @@
/*
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.
*/
#include <boost/scoped_ptr.hpp>
class filter_context;
class unit;
class vconfig;
struct map_location;
class unit_filter_abstract_impl {
public:
virtual bool matches(const unit & u, const map_location & loc) const = 0;
};
class unit_filter {
public:
unit_filter(const vconfig & cfg, const filter_context * fc, bool use_flat_tod = false); //!< Constructs a unit filter from a config and a context. This function should give the most efficient implementation available.
bool matches(const unit & u, const map_location & loc) const {
return impl_->matches(u,loc);
}
/// 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(const unit & u) const;
bool operator()(const unit & u, const map_location & loc) const {
return matches(u,loc);
}
bool operator()(const unit & u) const {
return matches(u);
}
private:
boost::scoped_ptr<unit_filter_abstract_impl> impl_;
};