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