00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
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
00042
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(),
00079
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
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 }
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
00121 if(!ignore_xy) {
00122 if(!loc.matches_range(cfg_["x"], cfg_["y"])) {
00123 return false;
00124 }
00125
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
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
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
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
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
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
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
00343 if(cond_name == "and")
00344 {
00345 matches = matches && terrain_filter(cond_cfg, *this)(*i);
00346 }
00347
00348 else if(cond_name == "or")
00349 {
00350 matches = matches || terrain_filter(cond_cfg, *this)(*i);
00351 }
00352
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
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
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
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
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
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
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
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
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
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
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
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 }