From 314b6cbfcc5cbee4456efd936732e35f59ded09c Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 15:18:45 -0400 Subject: [PATCH 01/13] fixup ignore_units option of teleport This was broken in this commit: 18159f0a6b4056ff4609ee4dcc8be4ff0018494f and caused a gcc warning which was fixed in this commit: ffbf683eb07ec320cea55f40d2ef5c740b422a34 --- src/pathfind/teleport.cpp | 57 +++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/src/pathfind/teleport.cpp b/src/pathfind/teleport.cpp index d561c0f8ffe3..74a525fcaa10 100644 --- a/src/pathfind/teleport.cpp +++ b/src/pathfind/teleport.cpp @@ -13,6 +13,8 @@ #include "pathfind/teleport.hpp" +#include "display_context.hpp" +#include "filter_context.hpp" #include "game_board.hpp" #include "log.hpp" #include "resources.hpp" @@ -60,28 +62,73 @@ teleport_group::teleport_group(const vconfig& cfg, bool reversed) : cfg_(cfg.get } } +class ignore_units_display_context : public display_context { +public: + ignore_units_display_context(const display_context & dc) + : um_() + , gm_(&dc.map()) + , tm_(&dc.teams()) + { + static unit_map empty_unit_map; + um_ = &empty_unit_map; + } + const unit_map & units() const { return *um_; } + const gamemap & map() const { return *gm_; } + const std::vector & teams() const { return *tm_; } + +private: + const unit_map * um_; + const gamemap * gm_; + const std::vector * tm_; +}; + +class ignore_units_filter_context : public filter_context { +public: + ignore_units_filter_context(const filter_context & fc) + : dc_(fc.get_disp_context()) + , tod_(&fc.get_tod_man()) + {} + + const display_context & get_disp_context() const { return dc_; } + const tod_manager & get_tod_man() const { return *tod_; } + +private: + const ignore_units_display_context dc_; + const tod_manager * tod_; +}; + void teleport_group::get_teleport_pair( teleport_pair& loc_pair , const unit& u - , const bool /*ignore_units*/) const + , const bool ignore_units) const { const map_location &loc = u.get_location(); - static unit_map empty_unit_map; + + const filter_context * fc = resources::filter_con; + assert(fc); + + if (ignore_units) { + fc = new ignore_units_filter_context(*resources::filter_con); + } vconfig filter(cfg_.child_or_empty("filter"), true); vconfig source(cfg_.child_or_empty("source"), true); vconfig target(cfg_.child_or_empty("target"), true); - const unit_filter ufilt(filter, resources::filter_con); + const unit_filter ufilt(filter, resources::filter_con); //Note: Don't use the ignore units filter context here, only for the terrain filters. (That's how it worked before the filter contexts were introduced) if (ufilt.matches(u, loc)) { scoped_xy_unit teleport_unit("teleport_unit", loc.x, loc.y, *resources::units); - terrain_filter source_filter(source, resources::filter_con); + terrain_filter source_filter(source, fc); source_filter.get_locations(reversed_ ? loc_pair.second : loc_pair.first); - terrain_filter target_filter(target, resources::filter_con); + terrain_filter target_filter(target, fc); target_filter.get_locations(reversed_ ? loc_pair.first : loc_pair.second); } + + if (ignore_units) { + delete fc; + } } const std::string& teleport_group::get_teleport_id() const { From 8bff0848a18a9dc122102b0e4a716b74cf6ea161 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 15:27:00 -0400 Subject: [PATCH 02/13] make the "alternate" clang build use -O0 optimizations The alternate clang build currently uses strict compilation = false, this was requested so that we get as many error messages as possible. We would also like to have an -O0 build run against the unit tests, as sometimes -O0 will segfault while -O2 won't, etc. So in this commit the alternate build uses that option also. I also rename the environment variables, so that the build matrix is based on casing out whether "alternate_configuration" is true (not strict_ compilation) as previously, and the alternate_configuration flag leads to options being exported in the before_install phase. --- .travis.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0aeee77d8445..15ab4d314580 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,17 +3,21 @@ compiler: - gcc - clang env: - - STRICT_COMPILATION=True - - STRICT_COMPILATION=False + - ALTERNATE_CONFIGURATION=true + - ALTERNATE_CONFIGURATION=false matrix: exclude: - compiler: gcc - env: STRICT_COMPILATION=False + env: ALTERNATE_CONFIGURATION=false before_install: - export TARGETS="wesnoth wesnothd campaignd test" - export WML_TESTS=true - export CPP_TESTS=true - export CHECK_UTF8=true + - export STRICT_COMPILATION=true + - export EXTRA_FLAGS_RELEASE="" + - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export STRICT_COMPILATION=false + - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export EXTRA_FLAGS_RELEASE="-O0" - if [ "$CXX" = "g++" ]; then export TARGETS="wesnoth test"; fi - if [ "$CXX" = "g++" ]; then export WML_TESTS=false; fi # - if [ "$CXX" = "g++" ]; then export CPP_TESTS=false; fi @@ -30,7 +34,7 @@ script: - if [ "$CHECK_UTF8" = true ]; then time ./utils/travis/check_utf8.sh; fi - time if grep -qorHbm1 "^`echo -ne '\xef\xbb\xbf'`" po/ src/ data/ ; then echo "Error, Found a UTF8 BOM:\n"; grep -orHbm1 "^`echo -ne '\xef\xbb\xbf'`" po/ src/ data/ ; exit 1; fi # UTF8 checks are the previous two lines. the second one checks po src data for UTF8 bom, this takes a few seconds. - - scons cxxtool=$CXX --debug=time strict=$STRICT_COMPILATION $TARGETS + - scons cxxtool=$CXX --debug=time build=release extra_flags_release="$EXRTA_FLAGS_RELEASE" strict=$STRICT_COMPILATION $TARGETS - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" - if [[ "$CPP_TESTS" = true ]]; then time ./utils/travis/test_wrapper.sh; fi From ebba6a3d11685444129fc54bbb0fa10007d5c2c3 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 15:30:28 -0400 Subject: [PATCH 03/13] refactor travis script, only call apt-get update once This moves the gcc 4.8 upgrade step to the "install" phase after we run apt-get update, so that we only do that once. It can take 15 sec or so apparently to execute that command. --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 15ab4d314580..add1c93b4312 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,12 +23,11 @@ before_install: # - if [ "$CXX" = "g++" ]; then export CPP_TESTS=false; fi - if [ "$CXX" = "g++" ]; then export CHECK_UTF8=false; fi - if [ "$CXX" = "g++" ]; then time sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y; fi - - if [ "$CXX" = "g++" ]; then time sudo apt-get update -qq; fi - - if [ "$CXX" = "g++" ]; then time sudo apt-get install g++-4.8; fi - - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8"; fi install: - time sudo apt-get update -qq - time sudo apt-get install -qq libboost-iostreams-dev libboost-program-options-dev libboost-regex-dev libboost-system-dev libboost-test-dev libcairo2-dev libfribidi-dev libpango1.0-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-net1.2-dev libsdl-ttf2.0-dev + - if [ "$CXX" = "g++" ]; then time sudo apt-get install g++-4.8; fi + - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8"; fi - if [ "$CHECK_UTF8" = true ]; then time sudo apt-get install -qq moreutils; fi script: - if [ "$CHECK_UTF8" = true ]; then time ./utils/travis/check_utf8.sh; fi From 31490868919d24f0f235ad7b331f3c13608ef3ab Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 15:35:17 -0400 Subject: [PATCH 04/13] fixup 8bff0848a18a The gcc build should not use the alternate configuration --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index add1c93b4312..cedf968693b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ env: matrix: exclude: - compiler: gcc - env: ALTERNATE_CONFIGURATION=false + env: ALTERNATE_CONFIGURATION=true before_install: - export TARGETS="wesnoth wesnothd campaignd test" - export WML_TESTS=true From 0f2dc9b17be706969b03a15a62a14e3ef3bc9387 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 15:36:35 -0400 Subject: [PATCH 05/13] use -O0 optimizations on the gcc build, as this is faster This swaps the optimization levels used by default and alternate configurations, so that gcc gets -O0, so the compiler will do less work. gcc does not run the wml unit tests so this should definitely save time. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index cedf968693b5..2fb7c3bdf043 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,9 +15,9 @@ before_install: - export CPP_TESTS=true - export CHECK_UTF8=true - export STRICT_COMPILATION=true - - export EXTRA_FLAGS_RELEASE="" + - export EXTRA_FLAGS_RELEASE="-O0" - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export STRICT_COMPILATION=false - - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export EXTRA_FLAGS_RELEASE="-O0" + - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export EXTRA_FLAGS_RELEASE="" - if [ "$CXX" = "g++" ]; then export TARGETS="wesnoth test"; fi - if [ "$CXX" = "g++" ]; then export WML_TESTS=false; fi # - if [ "$CXX" = "g++" ]; then export CPP_TESTS=false; fi From 07a067eb949e799a1a3c38ca707b65f062c4274a Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 15:45:32 -0400 Subject: [PATCH 06/13] fixup travis yml syntax errors (forgot ; fi) --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2fb7c3bdf043..b7fdbaca5c75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,8 @@ before_install: - export CHECK_UTF8=true - export STRICT_COMPILATION=true - export EXTRA_FLAGS_RELEASE="-O0" - - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export STRICT_COMPILATION=false - - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export EXTRA_FLAGS_RELEASE="" + - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export STRICT_COMPILATION=false; fi + - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export EXTRA_FLAGS_RELEASE=""; fi - if [ "$CXX" = "g++" ]; then export TARGETS="wesnoth test"; fi - if [ "$CXX" = "g++" ]; then export WML_TESTS=false; fi # - if [ "$CXX" = "g++" ]; then export CPP_TESTS=false; fi From c450d482f269c04a840111bb7eb5d75294e063cc Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 15:46:53 -0400 Subject: [PATCH 07/13] fixup typo in travis yml (EXRTA_FLAGS... -> EXTRA_FLAGS...) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b7fdbaca5c75..13637f6c06b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ script: - if [ "$CHECK_UTF8" = true ]; then time ./utils/travis/check_utf8.sh; fi - time if grep -qorHbm1 "^`echo -ne '\xef\xbb\xbf'`" po/ src/ data/ ; then echo "Error, Found a UTF8 BOM:\n"; grep -orHbm1 "^`echo -ne '\xef\xbb\xbf'`" po/ src/ data/ ; exit 1; fi # UTF8 checks are the previous two lines. the second one checks po src data for UTF8 bom, this takes a few seconds. - - scons cxxtool=$CXX --debug=time build=release extra_flags_release="$EXRTA_FLAGS_RELEASE" strict=$STRICT_COMPILATION $TARGETS + - scons cxxtool=$CXX --debug=time build=release extra_flags_release="$EXTRA_FLAGS_RELEASE" strict=$STRICT_COMPILATION $TARGETS - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" - if [[ "$CPP_TESTS" = true ]]; then time ./utils/travis/test_wrapper.sh; fi From 0ad27435184fd7ca2f9abbc270401132d6340977 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 16:06:29 -0400 Subject: [PATCH 08/13] mark destructors of derived classes virtual, in unit_filter.cpp This apparently produces msvc warnings if we don't do this. --- src/unit_filter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unit_filter.cpp b/src/unit_filter.cpp index 4593c1d4f664..f85830cf7418 100644 --- a/src/unit_filter.cpp +++ b/src/unit_filter.cpp @@ -53,7 +53,7 @@ class null_unit_filter_impl : public unit_filter_abstract_impl { return true; } - ~null_unit_filter_impl() {} + virtual ~null_unit_filter_impl() {} }; /// This enum helps to evaluate conditional filters @@ -97,7 +97,7 @@ class basic_unit_filter_impl : public unit_filter_abstract_impl { virtual bool matches(const unit & u, const map_location & loc) const; - ~basic_unit_filter_impl() {} + virtual ~basic_unit_filter_impl() {} private: const vconfig vcfg_; const filter_context & fc_; From 8943d742a86f78302d35cd3f32b699e99790e4ce Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 16:15:18 -0400 Subject: [PATCH 09/13] fixup abstract class definition --- src/unit_filter.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/unit_filter.hpp b/src/unit_filter.hpp index 06c9e3d82417..08f366962fc8 100644 --- a/src/unit_filter.hpp +++ b/src/unit_filter.hpp @@ -33,6 +33,7 @@ struct map_location; class unit_filter_abstract_impl { public: virtual bool matches(const unit & u, const map_location & loc) const = 0; + virtual ~unit_filter_abstract_impl() {} }; class unit_filter { From 3560cd85b372759adcabacca0a2661cd592a64b6 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 16:40:39 -0400 Subject: [PATCH 10/13] add a parameter to travis for test timeout, for use with -O0 --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 13637f6c06b7..c0c379152f28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,10 @@ before_install: - export CHECK_UTF8=true - export STRICT_COMPILATION=true - export EXTRA_FLAGS_RELEASE="-O0" + - export WML_TEST_TIME=30 - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export STRICT_COMPILATION=false; fi - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export EXTRA_FLAGS_RELEASE=""; fi + - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export WML_TEST_TIME=20; fi - if [ "$CXX" = "g++" ]; then export TARGETS="wesnoth test"; fi - if [ "$CXX" = "g++" ]; then export WML_TESTS=false; fi # - if [ "$CXX" = "g++" ]; then export CPP_TESTS=false; fi @@ -37,7 +39,7 @@ script: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" - if [[ "$CPP_TESTS" = true ]]; then time ./utils/travis/test_wrapper.sh; fi - - if [[ "$WML_TESTS" = true ]]; then time ./run_wml_tests -v -t 20; fi + - if [[ "$WML_TESTS" = true ]]; then time ./run_wml_tests -v -t "$WML_TEST_TIME"; fi after_failure: - if [ -f "errors.log" ]; then echo -e "\n*** \n*\n* Errors reported in wml unit tests, here is errors.log...\n*\n*** \n"; cat errors.log; fi notifications: From 91640422edbcf52f4951aa4fd15bb325fea36451 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 17:14:21 -0400 Subject: [PATCH 11/13] reenable campaignd and wesnothd compilation for travis gcc build Switching to -O0 seems to save us maybe 10 minutes (?) so we can probably afford this now --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c0c379152f28..9e9e752d9078 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,9 +20,7 @@ before_install: - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export STRICT_COMPILATION=false; fi - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export EXTRA_FLAGS_RELEASE=""; fi - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export WML_TEST_TIME=20; fi - - if [ "$CXX" = "g++" ]; then export TARGETS="wesnoth test"; fi - if [ "$CXX" = "g++" ]; then export WML_TESTS=false; fi -# - if [ "$CXX" = "g++" ]; then export CPP_TESTS=false; fi - if [ "$CXX" = "g++" ]; then export CHECK_UTF8=false; fi - if [ "$CXX" = "g++" ]; then time sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y; fi install: From 736d8bd287878ca0f1b38a7a3e33e3636c9b35d0 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Fri, 4 Jul 2014 17:33:23 -0400 Subject: [PATCH 12/13] more time for clang -O0 wml unit tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9e9e752d9078..9aa21f009ad7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ before_install: - export CHECK_UTF8=true - export STRICT_COMPILATION=true - export EXTRA_FLAGS_RELEASE="-O0" - - export WML_TEST_TIME=30 + - export WML_TEST_TIME=40 - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export STRICT_COMPILATION=false; fi - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export EXTRA_FLAGS_RELEASE=""; fi - if [ "$ALTERNATE_CONFIGURATION" = true ]; then export WML_TEST_TIME=20; fi From c9d05ff61bd417ae8da96cb5018709b5573abdf4 Mon Sep 17 00:00:00 2001 From: Chris Beck Date: Thu, 3 Jul 2014 19:20:38 -0400 Subject: [PATCH 13/13] more refactor of unit_filter as a function object It now successfully preconstructs subsidiary filters it uses, and discards the vcfg it was built from without copying it after construction. --- src/unit_filter.cpp | 323 +++++++++++++++++++++++++++----------------- 1 file changed, 201 insertions(+), 122 deletions(-) diff --git a/src/unit_filter.cpp b/src/unit_filter.cpp index f85830cf7418..68b407dc1991 100644 --- a/src/unit_filter.cpp +++ b/src/unit_filter.cpp @@ -32,7 +32,10 @@ #include "variable.hpp" // needed for vconfig, scoped unit #include +#include #include +#include //needed for boost::in_place to initialize optionals + #include ///Defined out of line to prevent including unit at unit_filter.hpp @@ -45,6 +48,10 @@ bool unit_filter::matches(const unit & u) const { // return false; //} + +/// Forward declare the "construct" method which constructs an appropriate filter impl +static unit_filter_abstract_impl * construct(const vconfig & vcfg, const filter_context & fc, bool flat_tod); + /// Null unit filter is built when the input config is null class null_unit_filter_impl : public unit_filter_abstract_impl { public: @@ -72,25 +79,92 @@ namespace conditional { 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) + : fc_(fc) , use_flat_tod_(flat_tod) + , cond_children_() + , cond_child_types_() + , cfg_name_(vcfg["name"]) + , cfg_id_(vcfg["id"]) + , cfg_speaker_(vcfg["speaker"]) + , cfg_filter_loc_(vcfg.has_child("filter_location") ? new terrain_filter(vconfig(vcfg.child("filter_location")), &fc_, use_flat_tod_) : NULL) + , cfg_filter_side_(vcfg.has_child("filter_side") ? new side_filter(vconfig(vcfg.child("filter_side")), &fc_) : NULL) //Note that it would be better to use boost optional here but it is apparently not possible to do in an initialiation list using boost::none, because when using ? the types must match, and side_filter is non-copyable + , cfg_x_(vcfg["x"]) + , cfg_y_(vcfg["y"]) + , cfg_type_(vcfg["type"]) + , cfg_variation_type_(vcfg["variation"]) + , cfg_has_variation_type_(vcfg["has_variation"]) + , cfg_ability_(vcfg["ability"]) + , cfg_race_(vcfg["race"]) + , cfg_gender_(vcfg["gender"]) + , cfg_side_(vcfg["side"]) + , cfg_has_weapon_(vcfg["has_weapon"]) + , cfg_role_(vcfg["role"]) + , cfg_ai_special_(vcfg["ai_special"]) + , cfg_canrecruit_(vcfg["canrecruit"]) + , cfg_recall_cost_(vcfg["recall_cost"]) + , cfg_level_(vcfg["level"]) + , cfg_defense_(vcfg["defense"]) + , cfg_movement_(vcfg["movement_cost"]) + , wmlcfgs_(vcfg.get_children("filter_wml")) + , vision_filters_viewers_lists_() + , vision_filters_visible_attr_() + , filter_adj_filters_() + , filter_adj_is_enemy_() + , filter_adj_dirs_() + , filter_adj_counts_() + , cfg_find_in_(vcfg["find_in"]) + , cfg_formula_(vcfg["formula"]) + , cfg_lua_function_(vcfg["lua_function"]) + { // 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(); + vconfig::all_children_iterator cond = vcfg.ordered_begin(); + vconfig::all_children_iterator cond_end = vcfg.ordered_end(); while(cond != cond_end) { + const std::string& cond_name = cond.get_key(); + 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_children_.push_back(new unit_filter(cond_filter, &fc_, use_flat_tod_)); cond_child_types_.push_back(type); - } catch (bad_enum_cast &) {} //ignore tags that aren't conditionals - + } catch (bad_enum_cast &) { // this means it isn't a conditional filter tag + + //while we are here, process filter_vision tags and filter_adjacent + if (cond_name == "filter_vision") + { + const vconfig& f = cond.get_child(); + vision_filters_visible_attr_.push_back(f["visible"].to_bool(true)); + + std::set viewers; + + // Use standard side filter + side_filter ssf(f, &fc_); + std::vector sides = ssf.get_teams(); + viewers.insert(sides.begin(), sides.end()); + + vision_filters_viewers_lists_.push_back(viewers); + } else if (cond_name == "filter_adjacent") { + const vconfig& f = cond.get_child(); + filter_adj_filters_.push_back(new unit_filter(f, &fc_, use_flat_tod_)); + + config::attribute_value i_adjacent = f["adjacent"]; + filter_adj_dirs_.push_back(!i_adjacent.blank() ? map_location::parse_directions(i_adjacent) : map_location::default_dirs()); + + config::attribute_value i_is_enemy = f["is_enemy"]; + if (i_is_enemy.blank()) { + filter_adj_is_enemy_.push_back(boost::none); + } else { + filter_adj_is_enemy_.push_back(i_is_enemy.to_bool()); + } + static std::vector > default_counts = utils::parse_ranges("1-6"); + config::attribute_value i_count = f["count"]; + filter_adj_counts_.push_back(!i_count.blank() ? utils::parse_ranges(i_count) : default_counts); + } + } ++cond; } } @@ -99,16 +173,65 @@ class basic_unit_filter_impl : public unit_filter_abstract_impl { virtual ~basic_unit_filter_impl() {} private: - const vconfig vcfg_; const filter_context & fc_; bool use_flat_tod_; - boost::ptr_vector cond_children_; + boost::ptr_vector cond_children_; std::vector cond_child_types_; + const config::attribute_value cfg_name_; + const config::attribute_value cfg_id_; + const config::attribute_value cfg_speaker_; + boost::scoped_ptr cfg_filter_loc_; + boost::scoped_ptr cfg_filter_side_; + const config::attribute_value cfg_x_; + const config::attribute_value cfg_y_; + const config::attribute_value cfg_type_; + const config::attribute_value cfg_variation_type_; + const config::attribute_value cfg_has_variation_type_; + const config::attribute_value cfg_ability_; + const config::attribute_value cfg_race_; + const config::attribute_value cfg_gender_; + const config::attribute_value cfg_side_; + const config::attribute_value cfg_has_weapon_; + const config::attribute_value cfg_role_; + const config::attribute_value cfg_ai_special_; + const config::attribute_value cfg_canrecruit_; + const config::attribute_value cfg_recall_cost_; + const config::attribute_value cfg_level_; + const config::attribute_value cfg_defense_; + const config::attribute_value cfg_movement_; + + const vconfig::child_list wmlcfgs_; + + std::vector > vision_filters_viewers_lists_; + std::vector vision_filters_visible_attr_; + + boost::ptr_vector filter_adj_filters_; + std::vector > filter_adj_is_enemy_; + std::vector > filter_adj_dirs_; + std::vector > > filter_adj_counts_; + + const config::attribute_value cfg_find_in_; + const config::attribute_value cfg_formula_; + const config::attribute_value cfg_lua_function_; + bool internal_matches_filter(const unit & u, const map_location & loc) const; }; +/** "Factory" method which constructs an appropriate implementation + * + */ + +static unit_filter_abstract_impl * construct(const vconfig & vcfg, const filter_context & fc, bool flat_tod) +{ + if (vcfg.null()) { + return new null_unit_filter_impl(); + } + return new basic_unit_filter_impl(vcfg, fc, flat_tod); + //TODO: Add more efficient implementations for special cases +} + /** Ctor of unit filter * unit_filter::unit_filter acts as a factory, selecting the appropriate implementation class */ @@ -117,11 +240,7 @@ unit_filter::unit_filter(const vconfig & vcfg, const filter_context * fc, bool f 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 + impl_.reset(construct(vcfg, *fc, flat_tod)); } /** Begin implementations of filter impl's @@ -157,14 +276,12 @@ bool basic_unit_filter_impl::matches(const unit & u, const map_location& loc) co 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()) { + 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; + if (!cfg_id_.blank()) { + const std::string& id = cfg_id_; const std::string& this_id = u.id(); if (id == this_id) { @@ -181,48 +298,40 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l } // 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()) { + 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)) { + if(cfg_filter_loc_) { + if(!cfg_filter_loc_->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())) + if(cfg_filter_side_) { + if(!cfg_filter_side_->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") { + 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()) { + } else if(cfg_x_.empty() && cfg_y_.empty()) { return false; - } else if(!loc.matches_range(cfg_x, cfg_y)) { + } 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()) + if (!cfg_type_.blank()) { - const std::string type_ids = cfg_type.str(); + 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, @@ -243,10 +352,9 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l } // The variation_type could be a comma separated list of types - config::attribute_value cfg_variation_type = vcfg_["variation"]; - if (!cfg_variation_type.blank()) + if (!cfg_variation_type_.blank()) { - const std::string type_ids = cfg_variation_type.str(); + 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, @@ -267,10 +375,9 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l } // 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()) + if (!cfg_has_variation_type_.blank()) { - const std::string& var_ids = cfg_has_variation_type.str(); + const std::string& var_ids = cfg_has_variation_type_.str(); const std::string& this_var = u.variation(); if ( var_ids == this_var ) { @@ -293,10 +400,9 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l } } - config::attribute_value cfg_ability = vcfg_["ability"]; - if (!cfg_ability.blank()) + if (!cfg_ability_.blank()) { - std::string ability = cfg_ability; + std::string ability = cfg_ability_; if(u.has_ability_by_id(ability)) { // pass } else if ( ability.find(',') != std::string::npos ) { @@ -316,9 +422,8 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l } } - config::attribute_value cfg_race = vcfg_["race"]; - if (!cfg_race.blank()) { - std::string race = cfg_race; + if (!cfg_race_.blank()) { + std::string race = cfg_race_; if(race != u.race()->id()) { const std::vector& vals = utils::split(race); @@ -328,14 +433,12 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l } } - config::attribute_value cfg_gender = vcfg_["gender"]; - if (!cfg_gender.blank() && string_gender(cfg_gender) != u.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 (!cfg_side_.blank() && cfg_side_.to_int() != u.side()) { + std::string side = cfg_side_; if ( side.find(',') == std::string::npos ) { return false; } @@ -345,9 +448,8 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l } } - config::attribute_value cfg_has_weapon = vcfg_["has_weapon"]; - if (!cfg_has_weapon.blank()) { - std::string weapon = cfg_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(); @@ -362,38 +464,31 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l } } - config::attribute_value cfg_role = vcfg_["role"]; - if (!cfg_role.blank() && cfg_role.str() != u.get_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))) { + 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()) { + 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()) { + 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()) { + 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))) { + 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))) { + if (!cfg_movement_.blank() && cfg_movement_.to_int(-1) != u.movement_cost(fc_.get_disp_context().map().get_terrain(loc))) { return false; } @@ -401,12 +496,11 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l // 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()) { + if (!wmlcfgs_.empty()) { config unit_cfg; - for (unsigned i = 0; i < wmlcfgs.size(); ++i) + for (unsigned i = 0; i < wmlcfgs_.size(); ++i) { - config fwml = wmlcfgs[i].get_parsed_config(); + 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(); @@ -425,67 +519,54 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l } } - 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; - } + assert(vision_filters_viewers_lists_.size() == vision_filters_visible_attr_.size()); + for (size_t i = 0; i < vision_filters_viewers_lists_.size(); i++) { + const std::set & viewers = vision_filters_viewers_lists_[i]; + 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 (vision_filters_visible_attr_[i] == unit_hidden) return false; } } - if (vcfg_.has_child("filter_adjacent")) { + assert(filter_adj_filters_.size() == filter_adj_is_enemy_.size()); + assert(filter_adj_filters_.size() == filter_adj_dirs_.size()); + assert(filter_adj_filters_.size() == filter_adj_counts_.size()); + if (filter_adj_filters_.size() > 0) { 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) { + + for (size_t i = 0; i < filter_adj_filters_.size(); 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(); + const std::vector & dirs = filter_adj_dirs_[i]; + 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)) { + if (unit_itor == units.end() || !filter_adj_filters_[i](*unit_itor)) { continue; } - config::attribute_value i_is_enemy = (*i)["is_enemy"]; - if (i_is_enemy.blank() || i_is_enemy.to_bool() == + if (!filter_adj_is_enemy_[i] || *filter_adj_is_enemy_[i] == 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)) { + + if(!in_ranges(match_count, filter_adj_counts_[i])) { return false; } } } - config::attribute_value cfg_find_in = vcfg_["find_in"]; - if (!cfg_find_in.blank()) { + 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); + 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; @@ -498,16 +579,14 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l return false; } } - config::attribute_value cfg_formula = vcfg_["formula"]; - if (!cfg_formula.blank()) { - if (!u.formula_manager().matches_filter(cfg_formula, loc, u)) { + 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 (!cfg_lua_function_.blank()) { + bool b = resources::lua_kernel->run_filter(cfg_lua_function_.str().c_str(), u); if (!b) return false; }