00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "global.hpp"
00016
00017 #include "actions.hpp"
00018 #include "config.hpp"
00019 #include "gamestatus.hpp"
00020 #include "log.hpp"
00021 #include "terrain_filter.hpp"
00022 #include "util.hpp"
00023
00024 #include <algorithm>
00025 #include <cassert>
00026 #include <cctype>
00027 #include <cstdlib>
00028 #include <iostream>
00029 #include <sstream>
00030
00031 #define ERR_CF LOG_STREAM(err, config)
00032 #define LOG_G LOG_STREAM(info, general)
00033 #define ERR_NG LOG_STREAM(err, engine)
00034
00035
00036 terrain_filter::terrain_filter(const vconfig& cfg, const gamemap& map,
00037 const gamestatus& game_status, const unit_map& units, const bool flat_tod,
00038 const size_t max_loop) : cfg_(cfg), map_(map), status_(game_status), units_(units)
00039 {
00040 restrict(max_loop);
00041 flatten(flat_tod);
00042 }
00043
00044 terrain_filter::terrain_filter(const vconfig& cfg, const terrain_filter& original)
00045 : cfg_(cfg), map_(original.map_), status_(original.status_), units_(original.units_)
00046 {
00047 restrict(original.max_loop_);
00048 flatten(original.flat_);
00049 }
00050
00051 terrain_filter::terrain_filter(const terrain_filter& other) :
00052 xy_pred(),
00053
00054 cfg_(other.cfg_),
00055 map_(other.map_),
00056 status_(other.status_),
00057 units_(other.units_)
00058 {
00059 restrict(other.max_loop_);
00060 flatten(other.flat_);
00061 }
00062
00063 terrain_filter& terrain_filter::operator=(const terrain_filter& other)
00064 {
00065
00066 if (this != &other) {
00067 this->~terrain_filter();
00068 new (this) terrain_filter(other) ;
00069 }
00070 return *this ;
00071 }
00072
00073 namespace {
00074 struct cfg_isor {
00075 bool operator() (std::pair<const std::string,const vconfig> val) {
00076 return val.first == "or";
00077 }
00078 };
00079 }
00080
00081 bool terrain_filter::match_internal(const gamemap::location& loc, const bool ignore_xy)
00082 {
00083 if(cfg_.has_attribute("terrain")) {
00084 if(cache_.parsed_terrain == NULL) {
00085 cache_.parsed_terrain = new t_translation::t_match(cfg_["terrain"]);
00086 }
00087 if(!cache_.parsed_terrain->is_empty) {
00088 const t_translation::t_terrain letter = map_.get_terrain_info(loc).number();
00089 if(!t_translation::terrain_matches(letter, *cache_.parsed_terrain)) {
00090 return false;
00091 }
00092 }
00093 }
00094
00095
00096 if(!ignore_xy) {
00097 if(!loc.matches_range(cfg_["x"], cfg_["y"])) {
00098 return false;
00099 }
00100
00101 if(cfg_.has_attribute("find_in")) {
00102 variable_info vi(cfg_["find_in"], false, variable_info::TYPE_CONTAINER);
00103 if(!vi.is_valid) return false;
00104 if(vi.explicit_index) {
00105 if(gamemap::location(vi.as_container(),NULL) != loc) {
00106 return false;
00107 }
00108 } else {
00109 variable_info::array_range a_range;
00110 for(a_range = vi.as_array(); a_range.first != a_range.second; ++a_range.first) {
00111 if(gamemap::location(**a_range.first,NULL) == loc) {
00112 break;
00113 }
00114 }
00115 if(a_range.first == a_range.second) {
00116 return false;
00117 }
00118 }
00119 }
00120 }
00121
00122
00123 if(cfg_.has_child("filter")) {
00124 const vconfig& unit_filter = cfg_.child("filter");
00125 const unit_map::const_iterator u = units_.find(loc);
00126 if (u == units_.end() || !u->second.matches_filter(unit_filter, loc, flat_))
00127 return false;
00128 }
00129
00130
00131 if(cfg_.has_child("filter_adjacent_location")) {
00132 gamemap::location adjacent[6];
00133 get_adjacent_tiles(loc, adjacent);
00134 const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
00135 vconfig::child_list::const_iterator i, i_end, i_begin = adj_cfgs.begin();
00136 for (i = i_begin, i_end = adj_cfgs.end(); i != i_end; ++i) {
00137 int match_count = 0;
00138 vconfig::child_list::difference_type index = i - i_begin;
00139 std::string adj_dirs = (*i).has_attribute("adjacent") ? (*i)["adjacent"]
00140 : "n,ne,se,s,sw,nw";
00141 static std::vector<gamemap::location::DIRECTION> default_dirs
00142 = gamemap::location::parse_directions("n,ne,se,s,sw,nw");
00143 std::vector<gamemap::location::DIRECTION> dirs = (*i).has_attribute("adjacent")
00144 ? gamemap::location::parse_directions((*i)["adjacent"]) : default_dirs;
00145 std::vector<gamemap::location::DIRECTION>::const_iterator j, j_end = dirs.end();
00146 for (j = dirs.begin(); j != j_end; ++j) {
00147 gamemap::location &adj = adjacent[*j];
00148 if(map_.on_board(adj)) {
00149 if(cache_.adjacent_matches == NULL) {
00150 while(index >= std::distance(cache_.adjacent_match_cache.begin(), cache_.adjacent_match_cache.end())) {
00151 const vconfig& adj_cfg = adj_cfgs[cache_.adjacent_match_cache.size()];
00152 std::pair<terrain_filter, std::map<gamemap::location,bool> > amc_pair(
00153 terrain_filter(adj_cfg, *this),
00154 std::map<gamemap::location,bool>());
00155 cache_.adjacent_match_cache.push_back(amc_pair);
00156 }
00157 terrain_filter &amc_filter = cache_.adjacent_match_cache[index].first;
00158 std::map<gamemap::location,bool> &amc = cache_.adjacent_match_cache[index].second;
00159 std::map<gamemap::location,bool>::iterator lookup = amc.find(adj);
00160 if(lookup == amc.end()) {
00161 if(amc_filter(adj)) {
00162 amc[adj] = true;
00163 ++match_count;
00164 } else {
00165 amc[adj] = false;
00166 }
00167 } else if(lookup->second) {
00168 ++match_count;
00169 }
00170 } else {
00171 assert(index < std::distance(cache_.adjacent_matches->begin(), cache_.adjacent_matches->end()));
00172 std::set<gamemap::location> &amc = (*cache_.adjacent_matches)[index];
00173 if(amc.find(adj) != amc.end()) {
00174 ++match_count;
00175 }
00176 }
00177 }
00178 }
00179 static std::vector<std::pair<int,int> > default_counts = utils::parse_ranges("1-6");
00180 std::vector<std::pair<int,int> > counts = (*i).has_attribute("count")
00181 ? utils::parse_ranges((*i)["count"]) : default_counts;
00182 std::vector<std::pair<int,int> >::const_iterator count, count_end = counts.end();
00183 bool count_matches = false;
00184 for (count = counts.begin(); count != count_end && !count_matches; ++count) {
00185 if(count->first <= match_count && match_count <= count->second) {
00186 count_matches = true;
00187 }
00188 }
00189 if(!count_matches) {
00190 return false;
00191 }
00192 }
00193 }
00194
00195 const t_string& t_tod_type = cfg_["time_of_day"];
00196 const t_string& t_tod_id = cfg_["time_of_day_id"];
00197 const std::string& tod_type = t_tod_type;
00198 const std::string& tod_id = t_tod_id;
00199 static config const dummy_cfg;
00200 time_of_day tod(dummy_cfg);
00201 if(!tod_type.empty() || !tod_id.empty()) {
00202 if(flat_) {
00203 tod = status_.get_time_of_day(0,loc);
00204 } else {
00205 tod = timeofday_at(status_,units_,loc, map_);
00206 }
00207 }
00208 if(!tod_type.empty()) {
00209 const std::vector<std::string>& vals = utils::split(tod_type);
00210 if(tod.lawful_bonus<0) {
00211 if(std::find(vals.begin(),vals.end(),"chaotic") == vals.end()) {
00212 return false;
00213 }
00214 } else if(tod.lawful_bonus>0) {
00215 if(std::find(vals.begin(),vals.end(),"lawful") == vals.end()) {
00216 return false;
00217 }
00218 } else {
00219 if(std::find(vals.begin(),vals.end(),"neutral") == vals.end()) {
00220 return false;
00221 }
00222 }
00223 }
00224 if(!tod_id.empty()) {
00225 if(tod_id != tod.id) {
00226 if(std::find(tod_id.begin(),tod_id.end(),',') != tod_id.end() &&
00227 std::search(tod_id.begin(),tod_id.end(),
00228 tod.id.begin(),tod.id.end()) != tod_id.end()) {
00229 const std::vector<std::string>& vals = utils::split(tod_id);
00230 if(std::find(vals.begin(),vals.end(),tod.id) == vals.end()) {
00231 return false;
00232 }
00233 } else {
00234 return false;
00235 }
00236 }
00237 }
00238
00239
00240 const t_string& t_owner_side = cfg_["owner_side"];
00241 const std::string& owner_side = t_owner_side;
00242 if(!owner_side.empty()) {
00243 const int side_index = lexical_cast_default<int>(owner_side,0) - 1;
00244 assert(status_.teams != NULL);
00245 if(village_owner(loc, *(status_.teams)) != side_index) {
00246 return false;
00247 }
00248 }
00249
00250 return true;
00251 }
00252
00253 bool terrain_filter::match(const gamemap::location& loc)
00254 {
00255 if(cfg_["x"] == "recall" && cfg_["y"] == "recall") {
00256 return !map_.on_board(loc);
00257 }
00258 std::set<gamemap::location> hexes;
00259 std::vector<gamemap::location> loc_vec(1, loc);
00260
00261
00262 size_t radius = lexical_cast_default<size_t>(cfg_["radius"], 0);
00263 if(radius > max_loop_) {
00264 ERR_NG << "terrain_filter: radius greater than " << max_loop_
00265 << ", restricting\n";
00266 radius = max_loop_;
00267 }
00268 if(cfg_.has_child("filter_radius")) {
00269 terrain_filter r_filter(cfg_.child("filter_radius"), *this);
00270 get_tiles_radius(map_, loc_vec, radius, hexes, &r_filter);
00271 } else {
00272 get_tiles_radius(map_, loc_vec, radius, hexes);
00273 }
00274
00275 size_t loop_count = 0;
00276 std::set<gamemap::location>::const_iterator i;
00277 for(i = hexes.begin(); i != hexes.end(); ++i) {
00278 bool matches = match_internal(*i, false);
00279
00280
00281 vconfig::all_children_iterator cond = cfg_.ordered_begin();
00282 vconfig::all_children_iterator cond_end = cfg_.ordered_end();
00283 while(cond != cond_end)
00284 {
00285 const std::string& cond_name = cond.get_key();
00286 const vconfig& cond_cfg = cond.get_child();
00287
00288
00289 if(cond_name == "and")
00290 {
00291 matches = matches && terrain_filter(cond_cfg, *this)(*i);
00292 }
00293
00294 else if(cond_name == "or")
00295 {
00296 matches = matches || terrain_filter(cond_cfg, *this)(*i);
00297 }
00298
00299 else if(cond_name == "not")
00300 {
00301 matches = matches && !terrain_filter(cond_cfg, *this)(*i);
00302 }
00303 ++cond;
00304 }
00305 if(matches) {
00306 return true;
00307 }
00308 if(++loop_count > max_loop_) {
00309 std::set<gamemap::location>::const_iterator temp = i;
00310 if(++temp != hexes.end()) {
00311 ERR_NG << "terrain_filter: loop count greater than " << max_loop_
00312 << ", aborting\n";
00313 break;
00314 }
00315 }
00316 }
00317 return false;
00318 }
00319
00320 void terrain_filter::get_locations(std::set<gamemap::location>& locs)
00321 {
00322 std::vector<gamemap::location> xy_vector = parse_location_range(cfg_["x"],cfg_["y"], &map_);
00323 std::set<gamemap::location> xy_set(xy_vector.begin(), xy_vector.end());
00324 if(xy_set.empty()) {
00325
00326 for(int x=0; x < map_.w(); x++) {
00327 for(int y=0; y < map_.h(); y++) {
00328 xy_set.insert(gamemap::location(x,y));
00329 }
00330 }
00331 }
00332 if(cfg_.has_attribute("find_in")) {
00333
00334 variable_info vi(cfg_["find_in"], false, variable_info::TYPE_CONTAINER);
00335 if(!vi.is_valid) {
00336 xy_set.clear();
00337 } else if(vi.explicit_index) {
00338 gamemap::location test_loc(vi.as_container(),NULL);
00339 if(xy_set.count(test_loc)) {
00340 xy_set.clear();
00341 xy_set.insert(test_loc);
00342 } else {
00343 xy_set.clear();
00344 }
00345 } else {
00346 std::set<gamemap::location> findin_locs;
00347 variable_info::array_range a_range;
00348 for(a_range = vi.as_array(); a_range.first != a_range.second; ++a_range.first) {
00349 gamemap::location test_loc(**a_range.first,NULL);
00350 if(xy_set.count(test_loc)) {
00351 findin_locs.insert(test_loc);
00352 }
00353 }
00354 xy_set.swap(findin_locs);
00355 }
00356 }
00357
00358
00359 if(cfg_.has_child("filter_adjacent_location")) {
00360 if(cache_.adjacent_matches == NULL) {
00361 cache_.adjacent_matches = new std::vector<std::set<gamemap::location> >();
00362 }
00363 const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
00364 for (unsigned i = 0; i < adj_cfgs.size(); ++i) {
00365 std::set<gamemap::location> adj_set;
00366
00367 terrain_filter(adj_cfgs.at(i), *this).get_locations(adj_set);
00368 cache_.adjacent_matches->push_back(adj_set);
00369 if(i >= max_loop_ && i+1 < adj_cfgs.size()) {
00370 ERR_NG << "terrain_filter: loop count greater than " << max_loop_
00371 << ", aborting\n";
00372 break;
00373 }
00374 }
00375 }
00376 std::set<gamemap::location>::iterator loc_itor = xy_set.begin();
00377 while(loc_itor != xy_set.end()) {
00378 if(match_internal(*loc_itor, true)) {
00379 ++loc_itor;
00380 } else {
00381 xy_set.erase(loc_itor++);
00382 }
00383 }
00384
00385
00386 vconfig::all_children_iterator cond = cfg_.ordered_begin();
00387 vconfig::all_children_iterator cond_end = cfg_.ordered_end();
00388 int ors_left = std::count_if(cond, cond_end, cfg_isor());
00389 while(cond != cond_end)
00390 {
00391
00392 if(xy_set.empty() && ors_left <= 0) {
00393 return;
00394 }
00395
00396 const std::string& cond_name = cond.get_key();
00397 const vconfig& cond_cfg = cond.get_child();
00398
00399
00400 if(cond_name == "and") {
00401 std::set<gamemap::location> intersect_hexes;
00402 terrain_filter(cond_cfg, *this).get_locations(intersect_hexes);
00403 std::set<gamemap::location>::iterator intersect_itor = xy_set.begin();
00404 while(intersect_itor != xy_set.end()) {
00405 if(intersect_hexes.find(*intersect_itor) == intersect_hexes.end()) {
00406 xy_set.erase(*intersect_itor++);
00407 } else {
00408 ++intersect_itor;
00409 }
00410 }
00411 }
00412
00413 else if(cond_name == "or") {
00414 std::set<gamemap::location> union_hexes;
00415 terrain_filter(cond_cfg, *this).get_locations(union_hexes);
00416
00417 std::set<gamemap::location>::iterator insert_itor = union_hexes.begin();
00418 while(insert_itor != union_hexes.end()) {
00419 xy_set.insert(*insert_itor++);
00420 }
00421 --ors_left;
00422 }
00423
00424 else if(cond_name == "not") {
00425 std::set<gamemap::location> removal_hexes;
00426 terrain_filter(cond_cfg, *this).get_locations(removal_hexes);
00427 std::set<gamemap::location>::iterator erase_itor = removal_hexes.begin();
00428 while(erase_itor != removal_hexes.end()) {
00429 xy_set.erase(*erase_itor++);
00430 }
00431 }
00432 ++cond;
00433 }
00434 if(xy_set.empty()) {
00435 return;
00436 }
00437
00438
00439 size_t radius = lexical_cast_default<size_t>(cfg_["radius"], 0);
00440 if(radius > max_loop_) {
00441 ERR_NG << "terrain_filter: radius greater than " << max_loop_
00442 << ", restricting\n";
00443 radius = max_loop_;
00444 }
00445 if(radius > 0) {
00446 xy_vector.clear();
00447 std::copy(xy_set.begin(),xy_set.end(),std::inserter(xy_vector,xy_vector.end()));
00448 if(cfg_.has_child("filter_radius")) {
00449 terrain_filter r_filter(cfg_.child("filter_radius"), *this);
00450 get_tiles_radius(map_, xy_vector, radius, locs, &r_filter);
00451 } else {
00452 get_tiles_radius(map_, xy_vector, radius, locs);
00453 }
00454 } else {
00455 std::copy(xy_set.begin(),xy_set.end(),std::inserter(locs,locs.end()));
00456 }
00457 }
00458
00459