terrain_filter.cpp

Go to the documentation of this file.
00001 /* $Id: terrain_filter.cpp 53763 2012-04-03 23:45:08Z jamit $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #define GETTEXT_DOMAIN "wesnoth-lib"
00017 
00018 #include "global.hpp"
00019 
00020 #include "actions.hpp"
00021 #include "config.hpp"
00022 #include "foreach.hpp"
00023 #include "log.hpp"
00024 #include "map.hpp"
00025 #include "resources.hpp"
00026 #include "side_filter.hpp"
00027 #include "team.hpp"
00028 #include "terrain_filter.hpp"
00029 #include "tod_manager.hpp"
00030 #include "variable.hpp"
00031 
00032 static lg::log_domain log_engine("engine");
00033 #define ERR_NG LOG_STREAM(err, log_engine)
00034 #define WRN_NG LOG_STREAM(warn, log_engine)
00035 
00036 terrain_filter::~terrain_filter()
00037 {
00038 }
00039 
00040 #ifdef _MSC_VER
00041 // This is a workaround for a VC bug; this constructor is never called
00042 // and so we don't care about the warnings this quick fix generates
00043 #pragma warning(push)
00044 #pragma warning(disable:4413)
00045 terrain_filter::terrain_filter():
00046     cfg_(vconfig::unconstructed_vconfig()),
00047     units_(unit_map()),
00048     cache_(),
00049     max_loop_(),
00050     flat_()
00051 {
00052     assert(false);
00053 }
00054 #pragma warning(pop)
00055 #endif
00056 
00057 
00058 terrain_filter::terrain_filter(const vconfig& cfg, const unit_map& units,
00059         const bool flat_tod, const size_t max_loop) :
00060     cfg_(cfg),
00061     units_(units),
00062     cache_(),
00063     max_loop_(max_loop),
00064     flat_(flat_tod)
00065 {
00066 }
00067 
00068 terrain_filter::terrain_filter(const vconfig& cfg, const terrain_filter& original) :
00069     cfg_(cfg),
00070     units_(original.units_),
00071     cache_(),
00072     max_loop_(original.max_loop_),
00073     flat_(original.flat_)
00074 {
00075 }
00076 
00077 terrain_filter::terrain_filter(const terrain_filter& other) :
00078     xy_pred(), // We should construct this too, since it has no datamembers
00079                // use the default constructor.
00080     cfg_(other.cfg_),
00081     units_(other.units_),
00082     cache_(),
00083     max_loop_(other.max_loop_),
00084     flat_(other.flat_)
00085 {
00086 }
00087 
00088 terrain_filter& terrain_filter::operator=(const terrain_filter& other)
00089 {
00090     // Use copy constructor to make sure we are coherant
00091     if (this != &other) {
00092         this->~terrain_filter();
00093         new (this) terrain_filter(other) ;
00094     }
00095     return *this ;
00096 }
00097 
00098 namespace {
00099     struct cfg_isor {
00100         bool operator() (std::pair<const std::string,const vconfig> val) {
00101             return val.first == "or";
00102         }
00103     };
00104 } //end anonymous namespace
00105 
00106 bool terrain_filter::match_internal(const map_location& loc, const bool ignore_xy) const
00107 {
00108     if(cfg_.has_attribute("terrain")) {
00109         if(cache_.parsed_terrain == NULL) {
00110             cache_.parsed_terrain = new t_translation::t_match(cfg_["terrain"]);
00111         }
00112         if(!cache_.parsed_terrain->is_empty) {
00113             const t_translation::t_terrain letter = resources::game_map->get_terrain_info(loc).number();
00114             if(!t_translation::terrain_matches(letter, *cache_.parsed_terrain)) {
00115                 return false;
00116             }
00117         }
00118     }
00119 
00120     //Allow filtering on location ranges
00121     if(!ignore_xy) {
00122         if(!loc.matches_range(cfg_["x"], cfg_["y"])) {
00123             return false;
00124         }
00125         //allow filtering by searching a stored variable of locations
00126         if(cfg_.has_attribute("find_in")) {
00127             variable_info vi(cfg_["find_in"], false, variable_info::TYPE_CONTAINER);
00128             if(!vi.is_valid) return false;
00129             if(vi.explicit_index) {
00130                 if(map_location(vi.as_container(),NULL) != loc) {
00131                     return false;
00132                 }
00133             } else {
00134                 bool found = false;
00135                 foreach (const config &cfg, vi.as_array()) {
00136                     if (map_location(cfg, NULL) == loc) {
00137                         found = true;
00138                         break;
00139                     }
00140                 }
00141                 if (!found) return false;
00142             }
00143         }
00144     }
00145 
00146     //Allow filtering on unit
00147     if(cfg_.has_child("filter")) {
00148         const vconfig& unit_filter = cfg_.child("filter");
00149         const unit_map::const_iterator u = units_.find(loc);
00150         if (u == units_.end() || !u->matches_filter(unit_filter, loc, flat_))
00151             return false;
00152     }
00153 
00154     // Allow filtering on visibility to a side
00155     if (cfg_.has_child("filter_vision")) {
00156         const vconfig::child_list& vis_filt = cfg_.get_children("filter_vision");
00157         vconfig::child_list::const_iterator i, i_end = vis_filt.end();
00158         for (i = vis_filt.begin(); i != i_end; ++i) {
00159             bool visible = (*i)["visible"].to_bool(true);
00160             bool respect_fog = (*i)["respect_fog"].to_bool(true);
00161 
00162             side_filter ssf(*i);
00163             std::vector<int> sides = ssf.get_teams();
00164 
00165             foreach(const int side, sides) {
00166                 const team &viewing_team = resources::teams->at(side - 1);
00167                 bool viewer_sees = respect_fog ? !viewing_team.fogged(loc) : !viewing_team.shrouded(loc);
00168                 if (visible != viewer_sees) {
00169                     return false;
00170                 }
00171             }
00172         }
00173     }
00174 
00175     //Allow filtering on adjacent locations
00176     if(cfg_.has_child("filter_adjacent_location")) {
00177         map_location adjacent[6];
00178         get_adjacent_tiles(loc, adjacent);
00179         const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
00180         vconfig::child_list::const_iterator i, i_end, i_begin = adj_cfgs.begin();
00181         for (i = i_begin, i_end = adj_cfgs.end(); i != i_end; ++i) {
00182             int match_count = 0;
00183             vconfig::child_list::difference_type index = i - i_begin;
00184             static std::vector<map_location::DIRECTION> default_dirs
00185                 = map_location::parse_directions("n,ne,se,s,sw,nw");
00186             std::vector<map_location::DIRECTION> dirs = (*i).has_attribute("adjacent")
00187                 ? map_location::parse_directions((*i)["adjacent"]) : default_dirs;
00188             std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
00189             for (j = dirs.begin(); j != j_end; ++j) {
00190                 map_location &adj = adjacent[*j];
00191                 if (resources::game_map->on_board(adj)) {
00192                     if(cache_.adjacent_matches == NULL) {
00193                         while(index >= std::distance(cache_.adjacent_match_cache.begin(), cache_.adjacent_match_cache.end())) {
00194                             const vconfig& adj_cfg = adj_cfgs[cache_.adjacent_match_cache.size()];
00195                             std::pair<terrain_filter, std::map<map_location,bool> > amc_pair(
00196                                 terrain_filter(adj_cfg, *this),
00197                                 std::map<map_location,bool>());
00198                             cache_.adjacent_match_cache.push_back(amc_pair);
00199                         }
00200                         terrain_filter &amc_filter = cache_.adjacent_match_cache[index].first;
00201                         std::map<map_location,bool> &amc = cache_.adjacent_match_cache[index].second;
00202                         std::map<map_location,bool>::iterator lookup = amc.find(adj);
00203                         if(lookup == amc.end()) {
00204                             if(amc_filter(adj)) {
00205                                 amc[adj] = true;
00206                                 ++match_count;
00207                             } else {
00208                                 amc[adj] = false;
00209                             }
00210                         } else if(lookup->second) {
00211                             ++match_count;
00212                         }
00213                     } else {
00214                         assert(index < std::distance(cache_.adjacent_matches->begin(), cache_.adjacent_matches->end()));
00215                         std::set<map_location> &amc = (*cache_.adjacent_matches)[index];
00216                         if(amc.find(adj) != amc.end()) {
00217                             ++match_count;
00218                         }
00219                     }
00220                 }
00221             }
00222             static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6");
00223             std::vector<std::pair<int,int> > counts = (*i).has_attribute("count")
00224                 ? utils::parse_ranges((*i)["count"]) : default_counts;
00225             if(!in_ranges(match_count, counts)) {
00226                 return false;
00227             }
00228         }
00229     }
00230 
00231     const t_string& t_tod_type = cfg_["time_of_day"];
00232     const t_string& t_tod_id = cfg_["time_of_day_id"];
00233     const std::string& tod_type = t_tod_type;
00234     const std::string& tod_id = t_tod_id;
00235     static config const dummy_cfg;
00236     time_of_day tod(dummy_cfg);
00237     if(!tod_type.empty() || !tod_id.empty()) {
00238         if(flat_) {
00239             tod = resources::tod_manager->get_time_of_day(loc);
00240         } else {
00241             tod = resources::tod_manager->get_illuminated_time_of_day(loc);
00242         }
00243     }
00244     if(!tod_type.empty()) {
00245         const std::vector<std::string>& vals = utils::split(tod_type);
00246         if(tod.lawful_bonus<0) {
00247             if(std::find(vals.begin(),vals.end(),std::string("chaotic")) == vals.end()) {
00248                 return false;
00249             }
00250         } else if(tod.lawful_bonus>0) {
00251             if(std::find(vals.begin(),vals.end(),std::string("lawful")) == vals.end()) {
00252                 return false;
00253             }
00254         } else if(std::find(vals.begin(),vals.end(),std::string("neutral")) == vals.end()) {
00255             return false;
00256         }
00257     }
00258 
00259     if(!tod_id.empty()) {
00260         if(tod_id != tod.id) {
00261             if(std::find(tod_id.begin(),tod_id.end(),',') != tod_id.end() &&
00262                 std::search(tod_id.begin(),tod_id.end(),
00263                 tod.id.begin(),tod.id.end()) != tod_id.end()) {
00264                 const std::vector<std::string>& vals = utils::split(tod_id);
00265                 if(std::find(vals.begin(),vals.end(),tod.id) == vals.end()) {
00266                     return false;
00267                 }
00268             } else {
00269                 return false;
00270             }
00271         }
00272     }
00273 
00274     //allow filtering on owner (for villages)
00275     const std::string owner_side = cfg_["owner_side"].str();
00276     const vconfig& filter_owner = cfg_.child("filter_owner");
00277     if(!filter_owner.null()) {
00278         if(!owner_side.empty()) {
00279             WRN_NG << "duplicate side information in a SLF, ignoring inline owner_side=\n";
00280         }
00281         if(!resources::game_map->is_village(loc))
00282             return false;
00283         side_filter ssf(filter_owner);
00284         const std::vector<int>& sides = ssf.get_teams();
00285         bool found = false;
00286         if(sides.empty() && village_owner(loc, *resources::teams) == -1)
00287             found = true;
00288         foreach(const int side, sides) {
00289             if(resources::teams->at(side - 1).owns_village(loc)) {
00290                 found = true;
00291                 break;
00292             }
00293         }
00294         if(!found)
00295             return false;
00296     }
00297     else if(!owner_side.empty()) {
00298         const int side_index = lexical_cast_default<int>(owner_side,0) - 1;
00299         if(village_owner(loc, *resources::teams) != side_index) {
00300             return false;
00301         }
00302     }
00303 
00304     return true;
00305 }
00306 
00307 bool terrain_filter::match(const map_location& loc) const
00308 {
00309     if(cfg_["x"] == "recall" && cfg_["y"] == "recall") {
00310         return !resources::game_map->on_board(loc);
00311     }
00312     std::set<map_location> hexes;
00313     std::vector<map_location> loc_vec(1, loc);
00314 
00315     //handle radius
00316     size_t radius = lexical_cast_default<size_t>(cfg_["radius"], 0);
00317     if(radius > max_loop_) {
00318         ERR_NG << "terrain_filter: radius greater than " << max_loop_
00319         << ", restricting\n";
00320         radius = max_loop_;
00321     }
00322     if(cfg_.has_child("filter_radius")) {
00323         terrain_filter r_filter(cfg_.child("filter_radius"), *this);
00324         get_tiles_radius(*resources::game_map, loc_vec, radius, hexes, false, &r_filter);
00325     } else {
00326         get_tiles_radius(*resources::game_map, loc_vec, radius, hexes);
00327     }
00328 
00329     size_t loop_count = 0;
00330     std::set<map_location>::const_iterator i;
00331     for(i = hexes.begin(); i != hexes.end(); ++i) {
00332         bool matches = match_internal(*i, false);
00333 
00334         //handle [and], [or], and [not] with in-order precedence
00335         vconfig::all_children_iterator cond = cfg_.ordered_begin();
00336         vconfig::all_children_iterator cond_end = cfg_.ordered_end();
00337         while(cond != cond_end)
00338         {
00339             const std::string& cond_name = cond.get_key();
00340             const vconfig& cond_cfg = cond.get_child();
00341 
00342             //handle [and]
00343             if(cond_name == "and")
00344             {
00345                 matches = matches && terrain_filter(cond_cfg, *this)(*i);
00346             }
00347             //handle [or]
00348             else if(cond_name == "or")
00349             {
00350                 matches = matches || terrain_filter(cond_cfg, *this)(*i);
00351             }
00352             //handle [not]
00353             else if(cond_name == "not")
00354             {
00355                 matches = matches && !terrain_filter(cond_cfg, *this)(*i);
00356             }
00357             ++cond;
00358         }
00359         if(matches) {
00360             return true;
00361         }
00362         if(++loop_count > max_loop_) {
00363             std::set<map_location>::const_iterator temp = i;
00364             if(++temp != hexes.end()) {
00365                 ERR_NG << "terrain_filter: loop count greater than " << max_loop_
00366                 << ", aborting\n";
00367                 break;
00368             }
00369         }
00370     }
00371     return false;
00372 }
00373 
00374 void terrain_filter::get_locations(std::set<map_location>& locs, bool with_border) const
00375 {
00376     std::vector<map_location> xy_vector =
00377         parse_location_range(cfg_["x"], cfg_["y"], with_border);
00378     std::set<map_location> xy_set(xy_vector.begin(), xy_vector.end());
00379     if (!cfg_.has_attribute("x") && !cfg_.has_attribute("y")) {
00380         //consider all locations on the map
00381         int bs = resources::game_map->border_size();
00382         int w = with_border ? resources::game_map->w() + bs : resources::game_map->w();
00383         int h = with_border ? resources::game_map->h() + bs : resources::game_map->h();
00384         for (int x = with_border ? 0 - bs : 0; x < w; ++x) {
00385             for (int y = with_border ? 0 - bs : 0; y < h; ++y) {
00386                 xy_set.insert(map_location(x,y));
00387             }
00388         }
00389     }
00390     if(cfg_.has_attribute("find_in")) {
00391         //remove any locations not found in the specified variable
00392         variable_info vi(cfg_["find_in"], false, variable_info::TYPE_CONTAINER);
00393         if(!vi.is_valid) {
00394             xy_set.clear();
00395         } else if(vi.explicit_index) {
00396             map_location test_loc(vi.as_container(),NULL);
00397             if(xy_set.count(test_loc)) {
00398                 xy_set.clear();
00399                 xy_set.insert(test_loc);
00400             } else {
00401                 xy_set.clear();
00402             }
00403         } else {
00404             std::set<map_location> findin_locs;
00405             foreach (const config &cfg, vi.as_array()) {
00406                 map_location test_loc(cfg, NULL);
00407                 if (xy_set.count(test_loc)) {
00408                     findin_locs.insert(test_loc);
00409                 }
00410             }
00411             xy_set.swap(findin_locs);
00412         }
00413     }
00414 
00415     //handle location filter
00416     if(cfg_.has_child("filter_adjacent_location")) {
00417         if(cache_.adjacent_matches == NULL) {
00418             cache_.adjacent_matches = new std::vector<std::set<map_location> >();
00419         }
00420         const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
00421         for (unsigned i = 0; i < adj_cfgs.size(); ++i) {
00422             std::set<map_location> adj_set;
00423             /* GCC-3.3 doesn't like operator[] so use at(), which has the same result */
00424             terrain_filter(adj_cfgs.at(i), *this).get_locations(adj_set, with_border);
00425             cache_.adjacent_matches->push_back(adj_set);
00426             if(i >= max_loop_ && i+1 < adj_cfgs.size()) {
00427                 ERR_NG << "terrain_filter: loop count greater than " << max_loop_
00428                 << ", aborting\n";
00429                 break;
00430             }
00431         }
00432     }
00433     std::set<map_location>::iterator loc_itor = xy_set.begin();
00434     while(loc_itor != xy_set.end()) {
00435         if(match_internal(*loc_itor, true)) {
00436             ++loc_itor;
00437         } else {
00438             xy_set.erase(loc_itor++);
00439         }
00440     }
00441 
00442     //handle [and], [or], and [not] with in-order precedence
00443     vconfig::all_children_iterator cond = cfg_.ordered_begin();
00444     vconfig::all_children_iterator cond_end = cfg_.ordered_end();
00445     int ors_left = std::count_if(cond, cond_end, cfg_isor());
00446     while(cond != cond_end)
00447     {
00448         //if there are no locations or [or] conditions left, go ahead and return empty
00449         if(xy_set.empty() && ors_left <= 0) {
00450             return;
00451         }
00452 
00453         const std::string& cond_name = cond.get_key();
00454         const vconfig& cond_cfg = cond.get_child();
00455 
00456         //handle [and]
00457         if(cond_name == "and") {
00458             std::set<map_location> intersect_hexes;
00459             terrain_filter(cond_cfg, *this).get_locations(intersect_hexes, with_border);
00460             std::set<map_location>::iterator intersect_itor = xy_set.begin();
00461             while(intersect_itor != xy_set.end()) {
00462                 if(intersect_hexes.find(*intersect_itor) == intersect_hexes.end()) {
00463                     xy_set.erase(*intersect_itor++);
00464                 } else {
00465                     ++intersect_itor;
00466                 }
00467             }
00468         }
00469         //handle [or]
00470         else if(cond_name == "or") {
00471             std::set<map_location> union_hexes;
00472             terrain_filter(cond_cfg, *this).get_locations(union_hexes, with_border);
00473             //xy_set.insert(union_hexes.begin(), union_hexes.end()); //doesn't compile on MSVC
00474             std::set<map_location>::iterator insert_itor = union_hexes.begin();
00475             while(insert_itor != union_hexes.end()) {
00476                 xy_set.insert(*insert_itor++);
00477             }
00478             --ors_left;
00479         }
00480         //handle [not]
00481         else if(cond_name == "not") {
00482             std::set<map_location> removal_hexes;
00483             terrain_filter(cond_cfg, *this).get_locations(removal_hexes, with_border);
00484             std::set<map_location>::iterator erase_itor = removal_hexes.begin();
00485             while(erase_itor != removal_hexes.end()) {
00486                 xy_set.erase(*erase_itor++);
00487             }
00488         }
00489         ++cond;
00490     }
00491     if(xy_set.empty()) {
00492         return;
00493     }
00494 
00495     //handle radius
00496     size_t radius = lexical_cast_default<size_t>(cfg_["radius"], 0);
00497     if(radius > max_loop_) {
00498         ERR_NG << "terrain_filter: radius greater than " << max_loop_
00499         << ", restricting\n";
00500         radius = max_loop_;
00501     }
00502     if(radius > 0) {
00503         xy_vector.clear();
00504         std::copy(xy_set.begin(),xy_set.end(),std::inserter(xy_vector,xy_vector.end()));
00505         if(cfg_.has_child("filter_radius")) {
00506             terrain_filter r_filter(cfg_.child("filter_radius"), *this);
00507             get_tiles_radius(*resources::game_map, xy_vector, radius, locs, with_border, &r_filter);
00508         } else {
00509             get_tiles_radius(*resources::game_map, xy_vector, radius, locs, with_border);
00510         }
00511     } else {
00512         std::copy(xy_set.begin(),xy_set.end(),std::inserter(locs,locs.end()));
00513     }
00514 }
00515 
00516 config terrain_filter::to_config() const
00517 {
00518     return cfg_.get_config();
00519 }
00520 
00521 terrain_filter::terrain_filter_cache::~terrain_filter_cache() {
00522     delete parsed_terrain;
00523     delete adjacent_matches;
00524 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:11 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs