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_; +};