The Battle for Wesnoth  1.15.3+dev
filter.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
17 #include "config.hpp"
18 #include "display_context.hpp"
19 #include "filter_context.hpp"
20 #include "game_board.hpp"
21 #include "game_data.hpp"
22 #include "log.hpp"
23 #include "map/map.hpp"
24 #include "side_filter.hpp"
25 #include "team.hpp"
26 #include "terrain/filter.hpp"
27 #include "tod_manager.hpp"
28 #include "units/unit.hpp"
29 #include "units/filter.hpp"
30 #include "units/alignment.hpp"
31 #include "variable.hpp"
32 #include "formula/callable_objects.hpp"
33 #include "formula/formula.hpp"
36 
37 #include <boost/range/adaptor/transformed.hpp>
38 
39 static lg::log_domain log_engine("engine");
40 #define ERR_NG LOG_STREAM(err, log_engine)
41 #define WRN_NG LOG_STREAM(warn, log_engine)
42 
44 {
45 }
46 
47 terrain_filter::terrain_filter(const vconfig& cfg, const filter_context * fc, const bool flat_tod) :
48  cfg_(cfg),
49  fc_(fc),
50  cache_(),
51  max_loop_(game_config::max_loop),
52  flat_(flat_tod)
53 {
54 }
55 
56 terrain_filter::terrain_filter(const vconfig& cfg, const terrain_filter& original) :
57  cfg_(cfg),
58  fc_(original.fc_),
59  cache_(),
60  max_loop_(original.max_loop_),
61  flat_(original.flat_)
62 {
63 }
64 
65 terrain_filter::terrain_filter(const terrain_filter& other) :
66  xy_pred(), // We should construct this too, since it has no datamembers
67  // use the default constructor.
68  cfg_(other.cfg_),
69  fc_(other.fc_),
70  cache_(),
71  max_loop_(other.max_loop_),
72  flat_(other.flat_)
73 {
74 }
75 
76 terrain_filter& terrain_filter::operator=(const terrain_filter& other)
77 {
78  // Use copy constructor to make sure we are coherent
79  if (this != &other) {
80  this->~terrain_filter();
81  new (this) terrain_filter(other) ;
82  }
83  return *this ;
84 }
85 
87  parsed_terrain(nullptr),
88  adjacent_matches(nullptr),
89  adjacent_match_cache(),
90  ufilter_()
91 {}
92 
93 namespace {
94  struct cfg_isor {
95  bool operator() (std::pair<const std::string,const vconfig> val) const {
96  return val.first == "or";
97  }
98  };
99 } //end anonymous namespace
100 
101 bool terrain_filter::match_internal(const map_location& loc, const unit* ref_unit, const bool ignore_xy) const
102 {
103  if (!this->fc_->get_disp_context().map().on_board_with_border(loc)) {
104  return false;
105  }
106 
107  std::string lua_function = cfg_["lua_function"];
108  if (!lua_function.empty() && fc_->get_lua_kernel()) {
109  if (!fc_->get_lua_kernel()->run_filter(lua_function.c_str(), loc)) {
110  return false;
111  }
112  }
113 
114  //Filter Areas
115  if (cfg_.has_attribute("area") &&
116  fc_->get_tod_man().get_area_by_id(cfg_["area"]).count(loc) == 0)
117  return false;
118 
119  if(cfg_.has_attribute("terrain")) {
120  if(cache_.parsed_terrain == nullptr) {
121  cache_.parsed_terrain.reset(new t_translation::ter_match(utils::string_view(cfg_["terrain"].str())));
122  }
123  if(!cache_.parsed_terrain->is_empty) {
124  const t_translation::terrain_code letter = fc_->get_disp_context().map().get_terrain_info(loc).number();
125  if(!t_translation::terrain_matches(letter, *cache_.parsed_terrain)) {
126  return false;
127  }
128  }
129  }
130 
131  //Allow filtering on location ranges
132  if (!ignore_xy) {
133  if (!loc.matches_range(cfg_["x"], cfg_["y"])) {
134  return false;
135  }
136  //allow filtering by searching a stored variable of locations
137  if (cfg_.has_attribute("find_in")) {
138  if (const game_data * gd = fc_->get_game_data()) {
139  try
140  {
141  variable_access_const vi = gd->get_variable_access_read(cfg_["find_in"]);
142 
143  bool found = false;
144  for (const config &cfg : vi.as_array()) {
145  if (map_location(cfg, nullptr) == loc) {
146  found = true;
147  break;
148  }
149  }
150  if (!found) return false;
151  }
152  catch (const invalid_variablename_exception&)
153  {
154  return false;
155  }
156  }
157  }
158  if (cfg_.has_attribute("location_id")) {
159  std::set<map_location> matching_locs;
160  for(const auto& id : utils::split(cfg_["location_id"])) {
161  map_location test_loc = fc_->get_disp_context().map().special_location(id);
162  if(test_loc.valid()) {
163  matching_locs.insert(test_loc);
164  }
165  }
166  if (matching_locs.count(loc) == 0) {
167  return false;
168  }
169  }
170  }
171  //Allow filtering on unit
172  if(cfg_.has_child("filter")) {
173  const unit_map::const_iterator u = fc_->get_disp_context().units().find(loc);
174  if (!u.valid())
175  return false;
176  if (!cache_.ufilter_) {
177  cache_.ufilter_.reset(new unit_filter(cfg_.child("filter").make_safe()));
178  cache_.ufilter_->set_use_flat_tod(flat_);
179  }
180  if (!cache_.ufilter_->matches(*u, loc))
181  return false;
182  }
183 
184  // Allow filtering on visibility to a side
185  if (cfg_.has_child("filter_vision")) {
186  const vconfig::child_list& vis_filt = cfg_.get_children("filter_vision");
187  vconfig::child_list::const_iterator i, i_end = vis_filt.end();
188  for (i = vis_filt.begin(); i != i_end; ++i) {
189  bool visible = (*i)["visible"].to_bool(true);
190  bool respect_fog = (*i)["respect_fog"].to_bool(true);
191 
192  side_filter ssf(*i, fc_);
193  std::vector<int> sides = ssf.get_teams();
194 
195  bool found = false;
196  for (const int side : sides) {
197  const team &viewing_team = fc_->get_disp_context().get_team(side);
198  bool viewer_sees = respect_fog ? !viewing_team.fogged(loc) : !viewing_team.shrouded(loc);
199  if (visible == viewer_sees) {
200  found = true;
201  break;
202  }
203  }
204  if (!found) {return false;}
205  }
206  }
207 
208  //Allow filtering on adjacent locations
209  if(cfg_.has_child("filter_adjacent_location")) {
210  adjacent_loc_array_t adjacent;
211  get_adjacent_tiles(loc, adjacent.data());
212  const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
213  vconfig::child_list::const_iterator i, i_end, i_begin = adj_cfgs.begin();
214  for (i = i_begin, i_end = adj_cfgs.end(); i != i_end; ++i) {
215  int match_count = 0;
216  vconfig::child_list::difference_type index = i - i_begin;
217  std::vector<map_location::DIRECTION> dirs = (*i).has_attribute("adjacent")
219  std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
220  for (j = dirs.begin(); j != j_end; ++j) {
221  map_location &adj = adjacent[*j];
222  if (fc_->get_disp_context().map().on_board(adj)) {
223  if(cache_.adjacent_matches == nullptr) {
224  while(index >= std::distance(cache_.adjacent_match_cache.begin(), cache_.adjacent_match_cache.end())) {
225  const vconfig& adj_cfg = adj_cfgs[cache_.adjacent_match_cache.size()];
226  std::pair<terrain_filter, std::map<map_location,bool>> amc_pair(
227  terrain_filter(adj_cfg, *this),
228  std::map<map_location,bool>());
229  cache_.adjacent_match_cache.push_back(amc_pair);
230  }
231  terrain_filter &amc_filter = cache_.adjacent_match_cache[index].first;
232  std::map<map_location,bool> &amc = cache_.adjacent_match_cache[index].second;
233  std::map<map_location,bool>::iterator lookup = amc.find(adj);
234  if(lookup == amc.end()) {
235  if(amc_filter(adj)) {
236  amc[adj] = true;
237  ++match_count;
238  } else {
239  amc[adj] = false;
240  }
241  } else if(lookup->second) {
242  ++match_count;
243  }
244  } else {
245  assert(index < std::distance(cache_.adjacent_matches->begin(), cache_.adjacent_matches->end()));
246  std::set<map_location> &amc = (*cache_.adjacent_matches)[index];
247  if(amc.find(adj) != amc.end()) {
248  ++match_count;
249  }
250  }
251  }
252  }
253  static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges("1-6");
254  std::vector<std::pair<int,int>> counts = (*i).has_attribute("count")
255  ? utils::parse_ranges((*i)["count"]) : default_counts;
256  if(!in_ranges(match_count, counts)) {
257  return false;
258  }
259  }
260  }
261 
262  const t_string& t_tod_type = cfg_["time_of_day"];
263  const t_string& t_tod_id = cfg_["time_of_day_id"];
264  const std::string& tod_type = t_tod_type;
265  const std::string& tod_id = t_tod_id;
266  if(!tod_type.empty() || !tod_id.empty()) {
267  // creating a time_of_day is expensive, only do it if we will use it
268  time_of_day tod;
269 
270  if(flat_) {
271  tod = fc_->get_tod_man().get_time_of_day(loc);
272  } else {
273  tod = fc_->get_tod_man().get_illuminated_time_of_day(fc_->get_disp_context().units(), fc_->get_disp_context().map(),loc);
274  }
275 
276  if(!tod_type.empty()) {
277  const std::vector<std::string>& vals = utils::split(tod_type);
278  if(tod.lawful_bonus<0) {
279  if(std::find(vals.begin(),vals.end(),UNIT_ALIGNMENT::enum_to_string(UNIT_ALIGNMENT::CHAOTIC)) == vals.end()) {
280  return false;
281  }
282  } else if(tod.lawful_bonus>0) {
283  if(std::find(vals.begin(),vals.end(),UNIT_ALIGNMENT::enum_to_string(UNIT_ALIGNMENT::LAWFUL)) == vals.end()) {
284  return false;
285  }
286  } else if(std::find(vals.begin(),vals.end(),UNIT_ALIGNMENT::enum_to_string(UNIT_ALIGNMENT::NEUTRAL)) == vals.end() &&
287  std::find(vals.begin(),vals.end(),UNIT_ALIGNMENT::enum_to_string(UNIT_ALIGNMENT::LIMINAL)) == vals.end()) {
288  return false;
289  }
290  }
291 
292  if(!tod_id.empty()) {
293  if(tod_id != tod.id) {
294  if(std::find(tod_id.begin(),tod_id.end(),',') != tod_id.end() &&
295  std::search(tod_id.begin(),tod_id.end(),
296  tod.id.begin(),tod.id.end()) != tod_id.end()) {
297  const std::vector<std::string>& vals = utils::split(tod_id);
298  if(std::find(vals.begin(),vals.end(),tod.id) == vals.end()) {
299  return false;
300  }
301  } else {
302  return false;
303  }
304  }
305  }
306  }
307 
308  //allow filtering on owner (for villages)
309  const config::attribute_value &owner_side = cfg_["owner_side"];
310  const vconfig& filter_owner = cfg_.child("filter_owner");
311  if(!filter_owner.null()) {
312  if(!owner_side.empty()) {
313  WRN_NG << "duplicate side information in a SLF, ignoring inline owner_side=" << std::endl;
314  }
315  if(!fc_->get_disp_context().map().is_village(loc))
316  return false;
317  side_filter ssf(filter_owner, fc_);
318  const std::vector<int>& sides = ssf.get_teams();
319  bool found = false;
320  if(sides.empty() && fc_->get_disp_context().village_owner(loc) == -1)
321  found = true;
322  for(const int side : sides) {
323  if(fc_->get_disp_context().get_team(side).owns_village(loc)) {
324  found = true;
325  break;
326  }
327  }
328  if(!found)
329  return false;
330  }
331  else if(!owner_side.empty()) {
332  const int side_index = owner_side.to_int(0) - 1;
333  if(fc_->get_disp_context().village_owner(loc) != side_index) {
334  return false;
335  }
336  }
337 
338  if(cfg_.has_attribute("formula")) {
339  try {
340  const wfl::terrain_callable main(fc_->get_disp_context(), loc);
341  wfl::map_formula_callable callable(main.fake_ptr());
342  if(ref_unit) {
343  auto ref = std::make_shared<wfl::unit_callable>(*ref_unit);
344  callable.add("teleport_unit", wfl::variant(ref));
345  // It's not destroyed upon scope exit because the variant holds a reference
346  }
348  const wfl::formula form(cfg_["formula"], &symbols);
349  if(!form.evaluate(callable).as_bool()) {
350  return false;
351  }
352  return true;
353  } catch(const wfl::formula_error& e) {
354  lg::wml_error() << "Formula error in location filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
355  // Formulae with syntax errors match nothing
356  return false;
357  }
358  }
359 
360  return true;
361 }
362 
363 class filter_with_unit : public xy_pred {
364  const terrain_filter& filt_;
365  const unit& ref_;
366 public:
367  filter_with_unit(const terrain_filter& filt, const unit& ref) : filt_(filt), ref_(ref) {}
368  bool operator()(const map_location& loc) const override {
369  return filt_.match(loc, ref_);
370  }
371 };
372 
373 bool terrain_filter::match_impl(const map_location& loc, const unit* ref_unit) const
374 {
375  if(cfg_["x"] == "recall" && cfg_["y"] == "recall") {
376  return !fc_->get_disp_context().map().on_board(loc);
377  }
378  std::set<map_location> hexes;
379  std::vector<map_location> loc_vec(1, loc);
380 
381  std::unique_ptr<scoped_wml_variable> ref_unit_var;
382  if(ref_unit) {
383  if(fc_->get_disp_context().map().on_board(ref_unit->get_location())) {
384  ref_unit_var.reset(new scoped_xy_unit("teleport_unit", ref_unit->get_location(), fc_->get_disp_context().units()));
385  } else {
386  // Possible TODO: Support recall list units?
387  }
388  }
389 
390  //handle radius
391  std::size_t radius = cfg_["radius"].to_size_t(0);
392  if(radius > max_loop_) {
393  ERR_NG << "terrain_filter: radius greater than " << max_loop_
394  << ", restricting\n";
395  radius = max_loop_;
396  }
397  if ( radius == 0 )
398  hexes.insert(loc_vec.begin(), loc_vec.end());
399  else if ( cfg_.has_child("filter_radius") ) {
400  terrain_filter r_filter(cfg_.child("filter_radius"), *this);
401  if(ref_unit) {
402  get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes, false, filter_with_unit(r_filter, *ref_unit));
403  } else {
404  get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes, false, r_filter);
405  }
406  } else {
407  get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes);
408  }
409 
410  std::size_t loop_count = 0;
411  std::set<map_location>::const_iterator i;
412  for(i = hexes.begin(); i != hexes.end(); ++i) {
413  bool matches = match_internal(*i, ref_unit, false);
414 
415  //handle [and], [or], and [not] with in-order precedence
416  vconfig::all_children_iterator cond = cfg_.ordered_begin();
417  vconfig::all_children_iterator cond_end = cfg_.ordered_end();
418  while(cond != cond_end)
419  {
420  const std::string& cond_name = cond.get_key();
421  const vconfig& cond_cfg = cond.get_child();
422 
423  //handle [and]
424  if(cond_name == "and")
425  {
426  matches = matches && terrain_filter(cond_cfg, *this).match_impl(*i, ref_unit);
427  }
428  //handle [or]
429  else if(cond_name == "or")
430  {
431  matches = matches || terrain_filter(cond_cfg, *this).match_impl(*i, ref_unit);
432  }
433  //handle [not]
434  else if(cond_name == "not")
435  {
436  matches = matches && !terrain_filter(cond_cfg, *this).match_impl(*i, ref_unit);
437  }
438  ++cond;
439  }
440  if(matches) {
441  return true;
442  }
443  if(++loop_count > max_loop_) {
444  std::set<map_location>::const_iterator temp = i;
445  if(++temp != hexes.end()) {
446  ERR_NG << "terrain_filter: loop count greater than " << max_loop_
447  << ", aborting\n";
448  break;
449  }
450  }
451  }
452  return false;
453 }
454 //using a class to be able to firen it in terrain_filter
456 {
457 public:
458  using location_set = std::set<map_location>;
459  struct no_start_set_yet {};
460  struct no_filter
461  {
462  bool operator()(const map_location&) const { return true; }
463  };
464 
465  template<typename T, typename F1, typename F2, typename F3>
466  static void filter_final(T&& src, location_set& dest, const terrain_filter&, const F1& f1, const F2& f2, const F3& f3)
467  {
468  for (const map_location &loc : src) {
469  if (f1(loc) && f2(loc) && f3(loc)) {
470  dest.insert(loc);
471  }
472  }
473  }
474 
475  template<typename T, typename F1, typename F2>
476  static void filter_special_loc(T&& src, location_set& dest, const terrain_filter& filter, const F1& f1, const F2& f2)
477  {
478  if (filter.cfg_.has_attribute("location_id")) {
479  std::set<map_location> matching_locs;
480  for(const auto& id : utils::split(filter.cfg_["location_id"])) {
481  map_location test_loc = filter.fc_->get_disp_context().map().special_location(id);
482  if(test_loc.valid()) {
483  matching_locs.insert(test_loc);
484  }
485  }
486  filter_final(src, dest, filter, f1, f2, [matching_locs](const map_location& loc) { return matching_locs.count(loc) > 0; });
487  }
488  else {
489  filter_final(src, dest, filter, f1, f2, no_filter());
490  }
491  }
492 
493  template<typename T, typename F1>
494  static void filter_area(T&& src, location_set& dest, const terrain_filter& filter, const F1& f1)
495  {
496  if (filter.cfg_.has_attribute("area")) {
497  const std::set<map_location>& area = filter.fc_->get_tod_man().get_area_by_id(filter.cfg_["area"]);
498  filter_special_loc(src, dest, filter, f1, [&area](const map_location& loc) { return area.find(loc) != area.end(); });
499  }
500  else {
501  filter_special_loc(src, dest, filter, f1, no_filter());
502  }
503  }
504 
505  template<typename T>
506  static void filter_xy(T&& src, location_set& dest, const terrain_filter& filter, bool with_border)
507  {
508  if (filter.cfg_.has_attribute("x") || filter.cfg_.has_attribute("y")) {
509  std::vector<map_location> xy_vector = filter.fc_->get_disp_context().map().parse_location_range(filter.cfg_["x"], filter.cfg_["y"], with_border);
510  filter_area(src, dest, filter, [&xy_vector](const map_location& loc) { return std::find(xy_vector.begin(), xy_vector.end(), loc) != xy_vector.end(); });
511  }
512  else {
513  filter_area(src, dest, filter, no_filter());
514  }
515  }
516 };
517 //using lambdas with boost transformed gives compile erros on gcc (it works on clang and msvc)
519 {
520  map_location operator()(const config& cfg) const { return map_location(cfg, nullptr); }
522 };
523 void terrain_filter::get_locs_impl(std::set<map_location>& locs, const unit* ref_unit, bool with_border) const
524 {
525  std::unique_ptr<scoped_wml_variable> ref_unit_var;
526  if(ref_unit) {
527  if(fc_->get_disp_context().map().on_board(ref_unit->get_location())) {
528  ref_unit_var.reset(new scoped_xy_unit("teleport_unit", ref_unit->get_location(), fc_->get_disp_context().units()));
529  } else {
530  // Possible TODO: Support recall list units?
531  }
532  }
533 
534  std::set<map_location> match_set;
535 
536  // See if the caller provided an override to with_border
537  with_border = cfg_["include_borders"].to_bool(with_border);
538 
539  if (cfg_.has_attribute("find_in")) {
540 
541  if (const game_data * gd = fc_->get_game_data()) {
542  try
543  {
544  auto ar = gd->get_variable_access_read(cfg_["find_in"]).as_array();
545  terrain_filterimpl::filter_xy(ar | boost::adaptors::transformed(cfg_to_loc()), match_set, *this, with_border);
546  }
547  catch (const invalid_variablename_exception&)
548  {
549  //Do nothing
550  }
551  }
552  }
553  else if (cfg_.has_attribute("x") || cfg_.has_attribute("y")) {
554  std::vector<map_location> xy_vector = fc_->get_disp_context().map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
555  terrain_filterimpl::filter_area(xy_vector, match_set, *this, terrain_filterimpl::no_filter());
556  }
557  else if (cfg_.has_attribute("area")) {
558  const std::set<map_location>& area = fc_->get_tod_man().get_area_by_id(cfg_["area"]);
560  }
561  else if (cfg_.has_attribute("location_id")) {
562  for(const auto& id : utils::split(cfg_["location_id"])) {
563  map_location test_loc = fc_->get_disp_context().map().special_location(id);
564  if(test_loc.valid()) {
565  match_set.insert(test_loc);
566  }
567  }
568  }
569  else {
570  //consider all locations on the map
571  int bs = fc_->get_disp_context().map().border_size();
572  int w = with_border ? fc_->get_disp_context().map().w() + bs : fc_->get_disp_context().map().w();
573  int h = with_border ? fc_->get_disp_context().map().h() + bs : fc_->get_disp_context().map().h();
574  for (int x = with_border ? 0 - bs : 0; x < w; ++x) {
575  for (int y = with_border ? 0 - bs : 0; y < h; ++y) {
576  match_set.insert(map_location(x, y));
577  }
578  }
579  }
580 
581  //handle location filter
582  if(cfg_.has_child("filter_adjacent_location")) {
583  if(cache_.adjacent_matches == nullptr) {
584  cache_.adjacent_matches.reset(new std::vector<std::set<map_location>>());
585  }
586  const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
587  for (unsigned i = 0; i < adj_cfgs.size(); ++i) {
588  std::set<map_location> adj_set;
589  /* GCC-3.3 doesn't like operator[] so use at(), which has the same result */
590  terrain_filter(adj_cfgs.at(i), *this).get_locations(adj_set, with_border);
591  cache_.adjacent_matches->push_back(adj_set);
592  if(i >= max_loop_ && i+1 < adj_cfgs.size()) {
593  ERR_NG << "terrain_filter: loop count greater than " << max_loop_
594  << ", aborting\n";
595  break;
596  }
597  }
598  }
599  std::set<map_location>::iterator loc_itor = match_set.begin();
600  while(loc_itor != match_set.end()) {
601  if(match_internal(*loc_itor, ref_unit, true)) {
602  ++loc_itor;
603  } else {
604  loc_itor = match_set.erase(loc_itor);
605  }
606  }
607 
608  //handle [and], [or], and [not] with in-order precedence
609  vconfig::all_children_iterator cond = cfg_.ordered_begin();
610  vconfig::all_children_iterator cond_end = cfg_.ordered_end();
611  int ors_left = std::count_if(cond, cond_end, cfg_isor());
612  while(cond != cond_end)
613  {
614  //if there are no locations or [or] conditions left, go ahead and return empty
615  if(match_set.empty() && ors_left <= 0) {
616  return;
617  }
618 
619  const std::string& cond_name = cond.get_key();
620  const vconfig& cond_cfg = cond.get_child();
621 
622  //handle [and]
623  if(cond_name == "and") {
624  std::set<map_location> intersect_hexes;
625  terrain_filter(cond_cfg, *this).get_locations(intersect_hexes, with_border);
626  std::set<map_location>::iterator intersect_itor = match_set.begin();
627  while(intersect_itor != match_set.end()) {
628  if(intersect_hexes.find(*intersect_itor) == intersect_hexes.end()) {
629  match_set.erase(*intersect_itor++);
630  } else {
631  ++intersect_itor;
632  }
633  }
634  }
635  //handle [or]
636  else if(cond_name == "or") {
637  std::set<map_location> union_hexes;
638  terrain_filter(cond_cfg, *this).get_locations(union_hexes, with_border);
639  //match_set.insert(union_hexes.begin(), union_hexes.end()); //doesn't compile on MSVC
640  std::set<map_location>::iterator insert_itor = union_hexes.begin();
641  while(insert_itor != union_hexes.end()) {
642  match_set.insert(*insert_itor++);
643  }
644  --ors_left;
645  }
646  //handle [not]
647  else if(cond_name == "not") {
648  std::set<map_location> removal_hexes;
649  terrain_filter(cond_cfg, *this).get_locations(removal_hexes, with_border);
650  std::set<map_location>::iterator erase_itor = removal_hexes.begin();
651  while(erase_itor != removal_hexes.end()) {
652  match_set.erase(*erase_itor++);
653  }
654  }
655  ++cond;
656  }
657  if(match_set.empty()) {
658  return;
659  }
660 
661  //handle radius
662  std::size_t radius = cfg_["radius"].to_size_t(0);
663  if(radius > max_loop_) {
664  ERR_NG << "terrain_filter: radius greater than " << max_loop_
665  << ", restricting\n";
666  radius = max_loop_;
667  }
668  if(radius > 0) {
669  std::vector<map_location> xy_vector (match_set.begin(), match_set.end());
670  if(cfg_.has_child("filter_radius")) {
671  terrain_filter r_filter(cfg_.child("filter_radius"), *this);
672  get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border, r_filter);
673  } else {
674  get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border);
675  }
676  } else {
677  locs.insert(match_set.begin(), match_set.end());
678  }
679 }
680 
682 {
683  return cfg_.get_config();
684 }
bool operator()(const map_location &) const
Definition: filter.cpp:462
std::function< int(lua_State *)> lua_function
bool empty() const
Tests for an attribute that either was never set or was set to "".
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:288
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:474
This class represents a single unit of a specific type.
Definition: unit.hpp:129
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:39
Variant for storing WML attributes.
bool terrain_matches(const terrain_code &src, const terrain_code &dest)
Tests whether a specific terrain matches an expression, for matching rules see above.
std::string filename
Definition: formula.hpp:107
static void filter_final(T &&src, location_set &dest, const terrain_filter &, const F1 &f1, const F2 &f2, const F3 &f3)
Definition: filter.cpp:466
map_location operator()(const config &cfg) const
Definition: filter.cpp:520
std::string id
Definition: time_of_day.hpp:91
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:84
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp >> &ranges)
Definition: math.hpp:86
#define h
static void filter_special_loc(T &&src, location_set &dest, const terrain_filter &filter, const F1 &f1, const F2 &f2)
Definition: filter.cpp:476
terrain_filter_cache cache_
Definition: filter.hpp:96
#define WRN_NG
Definition: filter.cpp:41
static std::vector< DIRECTION > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
Definition: location.cpp:124
const vconfig cfg_
Definition: filter.hpp:78
std::string get_key() const
Definition: variable.cpp:444
-file sdl_utils.hpp
const std::size_t max_loop
The maximum number of hexes on a map and items in an array and also used as maximum in wml loops...
Definition: game_config.cpp:92
Definitions for the interface to Wesnoth Markup Language (WML).
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
bool match_internal(const map_location &loc, const unit *ref_unit, const bool ignore_xy) const
Definition: filter.cpp:101
void get_locs_impl(std::set< map_location > &locs, const unit *ref_unit, bool with_border) const
Definition: filter.cpp:523
const filter_context * fc_
Definition: filter.hpp:79
config to_config() const
Definition: filter.cpp:681
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
std::vector< std::pair< int, int > > default_counts
const unit & ref_
Definition: filter.cpp:365
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:252
bool valid() const
Definition: location.hpp:93
std::string type
Definition: formula.hpp:105
static const std::vector< DIRECTION > & default_dirs()
Default list of directions.
Definition: location.cpp:52
terrain_filter(const vconfig &cfg, const filter_context *fc, const bool flat_tod=false)
std::array< map_location, 6 > adjacent_loc_array_t
Definition: location.hpp:170
const terrain_filter & filt_
Definition: filter.cpp:364
filter_with_unit(const terrain_filter &filt, const unit &ref)
Definition: filter.cpp:367
~terrain_filter()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: filter.cpp:43
Encapsulates the map of the game.
Definition: location.hpp:42
bool shrouded(const map_location &loc) const
Definition: team.cpp:653
std::size_t i
Definition: function.cpp:933
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:269
bool operator()(const map_location &loc) const override
Definition: filter.cpp:368
int main()
terrain_filter & operator=(const terrain_filter &other)
Definition: filter.cpp:76
void get_tiles_radius(const map_location &center, std::size_t radius, std::set< map_location > &result)
Function that will add to result all locations within radius tiles of center (including center itself...
Definition: pathutils.cpp:69
map_location result_type
Definition: filter.cpp:521
int w
bool matches_range(const std::string &xloc, const std::string &yloc) const
Definition: location.cpp:317
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
Information on a WML variable.
bool find(E event, F functor)
Tests whether an event handler is available.
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1336
static int cond(LexState *ls)
Definition: lparser.cpp:1177
std::vector< int > get_teams() const
Definition: side_filter.cpp:57
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
static lg::log_domain log_engine("engine")
Standard logging facilities (interface).
bool fogged(const map_location &loc) const
Definition: team.cpp:662
#define e
maybe_const_t< config::child_itors, V > as_array() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
static void filter_area(T &&src, location_set &dest, const terrain_filter &filter, const F1 &f1)
Definition: filter.cpp:494
bool match_impl(const map_location &loc, const unit *ref_unit) const
Definition: filter.cpp:373
std::vector< vconfig > child_list
Definition: variable.hpp:85
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:314
#define ERR_NG
Definition: filter.cpp:40
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
bool null() const
Definition: variable.hpp:80
bool valid() const
Definition: map.hpp:276
This structure can be used for matching terrain strings.
static void filter_xy(T &&src, location_set &dest, const terrain_filter &filter, bool with_border)
Definition: filter.cpp:506
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
const std::size_t max_loop_
Definition: filter.hpp:97
std::set< map_location > location_set
Definition: filter.cpp:458