diff --git a/data/test/scenarios/break_replay_with_lua_random.cfg b/data/test/scenarios/break_replay_with_lua_random.cfg
index c45b6cd9da5a..2ed0bd012749 100644
--- a/data/test/scenarios/break_replay_with_lua_random.cfg
+++ b/data/test/scenarios/break_replay_with_lua_random.cfg
@@ -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]
+)}
diff --git a/data/test/scenarios/event_handlers_in_events.cfg b/data/test/scenarios/event_handlers_in_events.cfg
index ea781140529e..61309e111d2e 100644
--- a/data/test/scenarios/event_handlers_in_events.cfg
+++ b/data/test/scenarios/event_handlers_in_events.cfg
@@ -189,7 +189,8 @@
name=post_advance
{VARIABLE pass_test 1}
[/my_event]
- [/variables])}
+ [/variables]
+)}
[store_unit]
[filter]
x=1
diff --git a/data/test/scenarios/move_skip_sighted.cfg b/data/test/scenarios/move_skip_sighted.cfg
index 2e5948d426b1..3dc809d5a0e7 100644
--- a/data/test/scenarios/move_skip_sighted.cfg
+++ b/data/test/scenarios/move_skip_sighted.cfg
@@ -61,7 +61,8 @@
id=bob
x={STOP_X}
y={STOP_Y}
- [/have_unit])}
+ [/have_unit]
+)}
[/event]
[/test]
#enddef
diff --git a/data/test/scenarios/test_unit_map.cfg b/data/test/scenarios/test_unit_map.cfg
index 1dcf50f30495..f96737aab9e1 100644
--- a/data/test/scenarios/test_unit_map.cfg
+++ b/data/test/scenarios/test_unit_map.cfg
@@ -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
diff --git a/data/test/scenarios/units_offmap_goto_recall.cfg b/data/test/scenarios/units_offmap_goto_recall.cfg
index 427bef270ad7..06a20fc94da9 100644
--- a/data/test/scenarios/units_offmap_goto_recall.cfg
+++ b/data/test/scenarios/units_offmap_goto_recall.cfg
@@ -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]
)}
diff --git a/projectfiles/CodeBlocks/wesnoth.cbp b/projectfiles/CodeBlocks/wesnoth.cbp
index d56e2a7110e8..94c78c2122ae 100644
--- a/projectfiles/CodeBlocks/wesnoth.cbp
+++ b/projectfiles/CodeBlocks/wesnoth.cbp
@@ -1039,6 +1039,8 @@
+
+
diff --git a/projectfiles/VC9/wesnoth.vcproj b/projectfiles/VC9/wesnoth.vcproj
index b4ec3dffd910..9a7942c15c08 100644
--- a/projectfiles/VC9/wesnoth.vcproj
+++ b/projectfiles/VC9/wesnoth.vcproj
@@ -21192,6 +21192,14 @@
RelativePath="..\..\src\unit_drawer.hpp"
>
+
+
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d4da828be3e8..d4f7ac899f3a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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
diff --git a/src/SConscript b/src/SConscript
index 1ff0c318d2c1..7fa19d90ceec 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -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
diff --git a/src/actions/create.cpp b/src/actions/create.cpp
index 6333546f23c3..6e8819c77b61 100644
--- a/src/actions/create.cpp
+++ b/src/actions/create.cpp
@@ -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.
diff --git a/src/ai/composite/goal.cpp b/src/ai/composite/goal.cpp
index 3c0eb8d10c13..d3f1399ba484 100644
--- a/src/ai/composite/goal.cpp
+++ b/src/ai/composite/goal.cpp
@@ -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
@@ -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(new terrain_filter(vconfig(criteria),*resources::units));
+ filter_ptr_ = boost::shared_ptr(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(new terrain_filter(vconfig(criteria),*resources::units));
+ filter_ptr_ = boost::shared_ptr(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 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());
}
diff --git a/src/ai/composite/value_translator.hpp b/src/ai/composite/value_translator.hpp
index 8a72c690b8f4..b66703e30402 100644
--- a/src/ai/composite/value_translator.hpp
+++ b/src/ai/composite/value_translator.hpp
@@ -179,10 +179,10 @@ class config_value_translator {
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 @@ class variant_value_translator {
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;
}
diff --git a/src/ai/contexts.cpp b/src/ai/contexts.cpp
index 809bbe4e281a..2e0b96b6cc36 100644
--- a/src/ai/contexts.cpp
+++ b/src/ai/contexts.cpp
@@ -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;
}
diff --git a/src/ai/lua/lua_object.hpp b/src/ai/lua/lua_object.hpp
index d6385e83c9d8..87947c225325 100644
--- a/src/ai/lua/lua_object.hpp
+++ b/src/ai/lua/lua_object.hpp
@@ -138,7 +138,7 @@ inline boost::shared_ptr lua_object::to_type(lua
boost::shared_ptr cfg = boost::shared_ptr(new config());
boost::shared_ptr vcfg = boost::shared_ptr(new vconfig(*cfg));
luaW_tovconfig(L, n, *vcfg);
- boost::shared_ptr tf = boost::shared_ptr(new terrain_filter(*vcfg, *resources::units));
+ boost::shared_ptr tf = boost::shared_ptr(new terrain_filter(*vcfg, resources::filter_con));
return tf;
}
diff --git a/src/ai/recruitment/recruitment.cpp b/src/ai/recruitment/recruitment.cpp
index 03f3a983bf9f..38e24080648b 100644
--- a/src/ai/recruitment/recruitment.cpp
+++ b/src/ai/recruitment/recruitment.cpp
@@ -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;
}
diff --git a/src/ai/testing/aspect_attacks.cpp b/src/ai/testing/aspect_attacks.cpp
index 404c84bcab24..0b7741a5bfa2 100644
--- a/src/ai/testing/aspect_attacks.cpp
+++ b/src/ai/testing/aspect_attacks.cpp
@@ -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 aspect_attacks::analyze_targets() const
unit_map& units_ = *resources::units;
std::vector 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 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;
diff --git a/src/display.cpp b/src/display.cpp
index 44d5cba5a7bf..3183dc878b70 100644
--- a/src/display.cpp
+++ b/src/display.cpp
@@ -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;
diff --git a/src/display.hpp b/src/display.hpp
index eb153563fdc1..484a7d63366b 100644
--- a/src/display.hpp
+++ b/src/display.hpp
@@ -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,
@@ -164,6 +165,7 @@ class display
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);
diff --git a/src/filter_context.hpp b/src/filter_context.hpp
new file mode 100644
index 000000000000..7af71c68fce6
--- /dev/null
+++ b/src/filter_context.hpp
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2014 by Chris Beck
+ 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
+
+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
diff --git a/src/game_board.hpp b/src/game_board.hpp
index 42d9adfc75ed..cad1a9958cff 100644
--- a/src/game_board.hpp
+++ b/src/game_board.hpp
@@ -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:
diff --git a/src/game_display.hpp b/src/game_display.hpp
index 76c678f91d60..59b5314b6683 100644
--- a/src/game_display.hpp
+++ b/src/game_display.hpp
@@ -130,6 +130,8 @@ class game_display : public display
bool has_time_area() const;
+ const tod_manager & get_tod_man() const { return tod_manager_; } //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 sides = s_filter.get_teams();
// Filter the locations.
std::set 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 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 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 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::iterator pi = resources::teams->begin();
pi!=resources::teams->end(); ++pi)
{
for(std::vector::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 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::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::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::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 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);
diff --git a/src/game_events/conditional_wml.cpp b/src/game_events/conditional_wml.cpp
index a16b8af512e0..fe814e55ebd5 100644
--- a/src/game_events/conditional_wml.cpp
+++ b/src/game_events/conditional_wml.cpp
@@ -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 > 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::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 res;
- terrain_filter(*v, *resources::units).get_locations(res);
+ terrain_filter(*v, resources::filter_con).get_locations(res);
std::vector > counts = (*v).has_attribute("count")
? utils::parse_ranges((*v)["count"]) : default_counts;
diff --git a/src/game_events/entity_location.cpp b/src/game_events/entity_location.cpp
index 806f2396d094..1d8bfb80c8ff 100644
--- a/src/game_events/entity_location.cpp
+++ b/src/game_events/entity_location.cpp
@@ -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);
}
diff --git a/src/game_events/menu_item.cpp b/src/game_events/menu_item.cpp
index 0f1bcbb3ac54..c3cc7919fe80 100644
--- a/src/game_events/menu_item.cpp
+++ b/src/game_events/menu_item.cpp
@@ -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.
diff --git a/src/game_events/pump.cpp b/src/game_events/pump.cpp
index 287d7bb3cfe2..447c80a1be7b 100644
--- a/src/game_events/pump.cpp
+++ b/src/game_events/pump.cpp
@@ -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;
}
diff --git a/src/game_state.hpp b/src/game_state.hpp
index 25928837256b..9629ee901ad3 100644
--- a/src/game_state.hpp
+++ b/src/game_state.hpp
@@ -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
diff --git a/src/pathfind/teleport.cpp b/src/pathfind/teleport.cpp
index fd4dab886c55..dda736148143 100644
--- a/src/pathfind/teleport.cpp
+++ b/src/pathfind/teleport.cpp
@@ -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
@@ -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);
}
}
diff --git a/src/play_controller.cpp b/src/play_controller.cpp
index ddfc34575690..7520f92d9236 100644
--- a/src/play_controller.cpp
+++ b/src/play_controller.cpp
@@ -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();
diff --git a/src/play_controller.hpp b/src/play_controller.hpp
index 4af404bd3510..68e60bc434da 100644
--- a/src/play_controller.hpp
+++ b/src/play_controller.hpp
@@ -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
{
diff --git a/src/resources.cpp b/src/resources.cpp
index 7c5b6f3db610..b47fdf8c2e53 100644
--- a/src/resources.cpp
+++ b/src/resources.cpp
@@ -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;
diff --git a/src/resources.hpp b/src/resources.hpp
index 2e87b1a84933..51e9dcec77c2 100644
--- a/src/resources.hpp
+++ b/src/resources.hpp
@@ -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 *teams;
diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp
index 234dfdf37057..e3e68371fbb8 100644
--- a/src/scripting/lua.cpp
+++ b/src/scripting/lua.cpp
@@ -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 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::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();
}
diff --git a/src/side_filter.cpp b/src/side_filter.cpp
index 1492df39f267..75074275e121 100644
--- a/src/side_filter.cpp
+++ b/src/side_filter.cpp
@@ -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
@@ -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 side_filter::get_teams() const
{
+ assert(fc_);
//@todo: replace with better implementation
std::vector 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& 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& 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;
}
diff --git a/src/side_filter.hpp b/src/side_filter.hpp
index 76309728a3c8..40245976f563 100644
--- a/src/side_filter.hpp
+++ b/src/side_filter.hpp
@@ -18,6 +18,7 @@
#include "variable.hpp"
class config;
+class filter_context;
class unit;
class unit_map;
class team;
@@ -35,8 +36,8 @@ class side_filter {
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 @@ class side_filter {
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
diff --git a/src/terrain_filter.cpp b/src/terrain_filter.cpp
index 663498e3890d..1958f619dcd3 100644
--- a/src/terrain_filter.cpp
+++ b/src/terrain_filter.cpp
@@ -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
@@ -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 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::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& 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 hexes;
std::vector 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& 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& locs, bool with_borde
&& !cfg_.has_attribute("area") ) {
std::vector 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& locs, bool with_borde
&& !cfg_.has_attribute("find_in")
&& cfg_.has_attribute("area") ) {
- const std::set& area = resources::tod_manager->get_area_by_id(cfg_["area"]);
+ const std::set& 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& locs, bool with_borde
&& !cfg_.has_attribute("area") ) {
std::vector 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& locs, bool with_borde
&& cfg_.has_attribute("area") ) {
std::vector xy_vector;
- xy_vector = resources::gameboard->map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
- const std::set& 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& 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& locs, bool with_borde
&& cfg_.has_attribute("find_in")
&& cfg_.has_attribute("area") ) {
- const std::set& area = resources::tod_manager->get_area_by_id(cfg_["area"]);
+ const std::set& 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& locs, bool with_borde
&& cfg_.has_attribute("area") ) {
const std::vector& 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 xy_set(xy_vector.begin(), xy_vector.end());
- const std::set& area = resources::tod_manager->get_area_by_id(cfg_["area"]);
+ const std::set& 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& locs, bool with_borde
std::vector 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());
diff --git a/src/terrain_filter.hpp b/src/terrain_filter.hpp
index 864c3b2429c2..3f3039cbb92e 100644
--- a/src/terrain_filter.hpp
+++ b/src/terrain_filter.hpp
@@ -21,6 +21,7 @@
#include "variable.hpp"
class config;
+class filter_context;
class unit;
class unit_map;
class team;
@@ -37,7 +38,7 @@ class terrain_filter : public xy_pred {
#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 @@ class terrain_filter : public xy_pred {
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() :
diff --git a/src/unit.cpp b/src/unit.cpp
index 87b78b592251..1cb630cfa4d8 100644
--- a/src/unit.cpp
+++ b/src/unit.cpp
@@ -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& 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& 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& 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& 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& vals = utils::split(ability);
- bool has_ability = false;
- for(std::vector::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& 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 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& attacks = this->attacks();
- for(std::vector::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 viewers;
- // Use standard side filter
- side_filter ssf(*i);
- std::vector sides = ssf.get_teams();
- viewers.insert(sides.begin(), sides.end());
- if (viewers.empty()) {
- return false;
- }
- std::set::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 dirs = !i_adjacent.blank() ?
- map_location::parse_directions(i_adjacent) : map_location::default_dirs();
- std::vector::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 > default_counts = utils::parse_ranges("1-6");
- config::attribute_value i_count = (*i)["count"];
- std::vector > 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"];
diff --git a/src/unit.hpp b/src/unit.hpp
index 08447ee4311d..d2f3a3701c81 100644
--- a/src/unit.hpp
+++ b/src/unit.hpp
@@ -137,6 +137,7 @@ class unit
/** 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 @@ class unit
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& overlays() const { return overlays_; }
void write(config& cfg) const;
@@ -382,9 +377,6 @@ class unit
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 @@ class unit
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*/
diff --git a/src/unit_abilities.cpp b/src/unit_abilities.cpp
index 0eb01d544ef7..354cdb6a9d56 100644
--- a/src/unit_abilities.cpp
+++ b/src/unit_abilities.cpp
@@ -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
@@ -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 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;
}
diff --git a/src/unit_animation.cpp b/src/unit_animation.cpp
index 37feef62b5ad..9e6df5b17c20 100644
--- a/src/unit_animation.cpp
+++ b/src/unit_animation.cpp
@@ -23,6 +23,7 @@
#include "resources.hpp"
#include "unit.hpp"
#include "unit_animation_component.hpp"
+#include "unit_filter.hpp"
#include "variable.hpp"
#include
@@ -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::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::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++;
}
diff --git a/src/unit_display.cpp b/src/unit_display.cpp
index 39923aa6e4c7..73b3d3fccf06 100644
--- a/src/unit_display.cpp
+++ b/src/unit_display.cpp
@@ -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
@@ -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 locs;
filter.get_locations(locs);
if (!locs.empty() && u->get_location() != *locs.begin()) {
diff --git a/src/unit_filter.cpp b/src/unit_filter.cpp
new file mode 100644
index 000000000000..4593c1d4f664
--- /dev/null
+++ b/src/unit_filter.cpp
@@ -0,0 +1,515 @@
+/*
+ Copyright (C) 2014 by Chris Beck
+ 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
+#include
+#include
+
+///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 cond_children_;
+ std::vector 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& 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& 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& 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& 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& vals = utils::split(ability);
+ bool has_ability = false;
+ for(std::vector::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& 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 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& attacks = u.attacks();
+ for(std::vector::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 viewers;
+ // Use standard side filter
+ side_filter ssf(*i, &fc_);
+ std::vector sides = ssf.get_teams();
+ viewers.insert(sides.begin(), sides.end());
+ if (viewers.empty()) {
+ return false;
+ }
+ std::set::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 dirs = !i_adjacent.blank() ?
+ map_location::parse_directions(i_adjacent) : map_location::default_dirs();
+ std::vector::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 > default_counts = utils::parse_ranges("1-6");
+ config::attribute_value i_count = (*i)["count"];
+ std::vector > 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;
+}
diff --git a/src/unit_filter.hpp b/src/unit_filter.hpp
new file mode 100644
index 000000000000..06c9e3d82417
--- /dev/null
+++ b/src/unit_filter.hpp
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 2014 by Chris Beck
+ 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
+
+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 impl_;
+};