The Battle for Wesnoth  1.15.2+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  std::find(vals.begin(),vals.end(),unit_type::ALIGNMENT::enum_to_string(unit_type::ALIGNMENT::LIMINAL)) == vals.end()) {
287  return false;
288  }
289  }
290 
291  if(!tod_id.empty()) {
292  if(tod_id != tod.id) {
293  if(std::find(tod_id.begin(),tod_id.end(),',') != tod_id.end() &&
294  std::search(tod_id.begin(),tod_id.end(),
295  tod.id.begin(),tod.id.end()) != tod_id.end()) {
296  const std::vector<std::string>& vals = utils::split(tod_id);
297  if(std::find(vals.begin(),vals.end(),tod.id) == vals.end()) {
298  return false;
299  }
300  } else {
301  return false;
302  }
303  }
304  }
305  }
306 
307  //allow filtering on owner (for villages)
308  const config::attribute_value &owner_side = cfg_["owner_side"];
309  const vconfig& filter_owner = cfg_.child("filter_owner");
310  if(!filter_owner.null()) {
311  if(!owner_side.empty()) {
312  WRN_NG << "duplicate side information in a SLF, ignoring inline owner_side=" << std::endl;
313  }
314  if(!fc_->get_disp_context().map().is_village(loc))
315  return false;
316  side_filter ssf(filter_owner, fc_);
317  const std::vector<int>& sides = ssf.get_teams();
318  bool found = false;
319  if(sides.empty() && fc_->get_disp_context().village_owner(loc) == -1)
320  found = true;
321  for(const int side : sides) {
322  if(fc_->get_disp_context().get_team(side).owns_village(loc)) {
323  found = true;
324  break;
325  }
326  }
327  if(!found)
328  return false;
329  }
330  else if(!owner_side.empty()) {
331  const int side_index = owner_side.to_int(0) - 1;
332  if(fc_->get_disp_context().village_owner(loc) != side_index) {
333  return false;
334  }
335  }
336 
337  if(cfg_.has_attribute("formula")) {
338  try {
339  const wfl::terrain_callable main(fc_->get_disp_context(), loc);
340  wfl::map_formula_callable callable(main.fake_ptr());
341  if(ref_unit) {
342  auto ref = std::make_shared<wfl::unit_callable>(*ref_unit);
343  callable.add("teleport_unit", wfl::variant(ref));
344  // It's not destroyed upon scope exit because the variant holds a reference
345  }
347  const wfl::formula form(cfg_["formula"], &symbols);
348  if(!form.evaluate(callable).as_bool()) {
349  return false;
350  }
351  return true;
352  } catch(const wfl::formula_error& e) {
353  lg::wml_error() << "Formula error in location filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
354  // Formulae with syntax errors match nothing
355  return false;
356  }
357  }
358 
359  return true;
360 }
361 
362 class filter_with_unit : public xy_pred {
363  const terrain_filter& filt_;
364  const unit& ref_;
365 public:
366  filter_with_unit(const terrain_filter& filt, const unit& ref) : filt_(filt), ref_(ref) {}
367  bool operator()(const map_location& loc) const override {
368  return filt_.match(loc, ref_);
369  }
370 };
371 
372 bool terrain_filter::match_impl(const map_location& loc, const unit* ref_unit) const
373 {
374  if(cfg_["x"] == "recall" && cfg_["y"] == "recall") {
375  return !fc_->get_disp_context().map().on_board(loc);
376  }
377  std::set<map_location> hexes;
378  std::vector<map_location> loc_vec(1, loc);
379 
380  std::unique_ptr<scoped_wml_variable> ref_unit_var;
381  if(ref_unit) {
382  if(fc_->get_disp_context().map().on_board(ref_unit->get_location())) {
383  ref_unit_var.reset(new scoped_xy_unit("teleport_unit", ref_unit->get_location(), fc_->get_disp_context().units()));
384  } else {
385  // Possible TODO: Support recall list units?
386  }
387  }
388 
389  //handle radius
390  std::size_t radius = cfg_["radius"].to_size_t(0);
391  if(radius > max_loop_) {
392  ERR_NG << "terrain_filter: radius greater than " << max_loop_
393  << ", restricting\n";
394  radius = max_loop_;
395  }
396  if ( radius == 0 )
397  hexes.insert(loc_vec.begin(), loc_vec.end());
398  else if ( cfg_.has_child("filter_radius") ) {
399  terrain_filter r_filter(cfg_.child("filter_radius"), *this);
400  if(ref_unit) {
401  get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes, false, filter_with_unit(r_filter, *ref_unit));
402  } else {
403  get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes, false, r_filter);
404  }
405  } else {
406  get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes);
407  }
408 
409  std::size_t loop_count = 0;
410  std::set<map_location>::const_iterator i;
411  for(i = hexes.begin(); i != hexes.end(); ++i) {
412  bool matches = match_internal(*i, ref_unit, false);
413 
414  //handle [and], [or], and [not] with in-order precedence
415  vconfig::all_children_iterator cond = cfg_.ordered_begin();
416  vconfig::all_children_iterator cond_end = cfg_.ordered_end();
417  while(cond != cond_end)
418  {
419  const std::string& cond_name = cond.get_key();
420  const vconfig& cond_cfg = cond.get_child();
421 
422  //handle [and]
423  if(cond_name == "and")
424  {
425  matches = matches && terrain_filter(cond_cfg, *this).match_impl(*i, ref_unit);
426  }
427  //handle [or]
428  else if(cond_name == "or")
429  {
430  matches = matches || terrain_filter(cond_cfg, *this).match_impl(*i, ref_unit);
431  }
432  //handle [not]
433  else if(cond_name == "not")
434  {
435  matches = matches && !terrain_filter(cond_cfg, *this).match_impl(*i, ref_unit);
436  }
437  ++cond;
438  }
439  if(matches) {
440  return true;
441  }
442  if(++loop_count > max_loop_) {
443  std::set<map_location>::const_iterator temp = i;
444  if(++temp != hexes.end()) {
445  ERR_NG << "terrain_filter: loop count greater than " << max_loop_
446  << ", aborting\n";
447  break;
448  }
449  }
450  }
451  return false;
452 }
453 //using a class to be able to firen it in terrain_filter
455 {
456 public:
457  using location_set = std::set<map_location>;
458  struct no_start_set_yet {};
459  struct no_filter
460  {
461  bool operator()(const map_location&) const { return true; }
462  };
463 
464  template<typename T, typename F1, typename F2, typename F3>
465  static void filter_final(T&& src, location_set& dest, const terrain_filter&, const F1& f1, const F2& f2, const F3& f3)
466  {
467  for (const map_location &loc : src) {
468  if (f1(loc) && f2(loc) && f3(loc)) {
469  dest.insert(loc);
470  }
471  }
472  }
473 
474  template<typename T, typename F1, typename F2>
475  static void filter_special_loc(T&& src, location_set& dest, const terrain_filter& filter, const F1& f1, const F2& f2)
476  {
477  if (filter.cfg_.has_attribute("location_id")) {
478  std::set<map_location> matching_locs;
479  for(const auto& id : utils::split(filter.cfg_["location_id"])) {
480  map_location test_loc = filter.fc_->get_disp_context().map().special_location(id);
481  if(test_loc.valid()) {
482  matching_locs.insert(test_loc);
483  }
484  }
485  filter_final(src, dest, filter, f1, f2, [matching_locs](const map_location& loc) { return matching_locs.count(loc) > 0; });
486  }
487  else {
488  filter_final(src, dest, filter, f1, f2, no_filter());
489  }
490  }
491 
492  template<typename T, typename F1>
493  static void filter_area(T&& src, location_set& dest, const terrain_filter& filter, const F1& f1)
494  {
495  if (filter.cfg_.has_attribute("area")) {
496  const std::set<map_location>& area = filter.fc_->get_tod_man().get_area_by_id(filter.cfg_["area"]);
497  filter_special_loc(src, dest, filter, f1, [&area](const map_location& loc) { return area.find(loc) != area.end(); });
498  }
499  else {
500  filter_special_loc(src, dest, filter, f1, no_filter());
501  }
502  }
503 
504  template<typename T>
505  static void filter_xy(T&& src, location_set& dest, const terrain_filter& filter, bool with_border)
506  {
507  if (filter.cfg_.has_attribute("x") || filter.cfg_.has_attribute("y")) {
508  std::vector<map_location> xy_vector = filter.fc_->get_disp_context().map().parse_location_range(filter.cfg_["x"], filter.cfg_["y"], with_border);
509  filter_area(src, dest, filter, [&xy_vector](const map_location& loc) { return std::find(xy_vector.begin(), xy_vector.end(), loc) != xy_vector.end(); });
510  }
511  else {
512  filter_area(src, dest, filter, no_filter());
513  }
514  }
515 };
516 //using lambdas with boost transformed gives compile erros on gcc (it works on clang and msvc)
518 {
519  map_location operator()(const config& cfg) const { return map_location(cfg, nullptr); }
521 };
522 void terrain_filter::get_locs_impl(std::set<map_location>& locs, const unit* ref_unit, bool with_border) const
523 {
524  std::unique_ptr<scoped_wml_variable> ref_unit_var;
525  if(ref_unit) {
526  if(fc_->get_disp_context().map().on_board(ref_unit->get_location())) {
527  ref_unit_var.reset(new scoped_xy_unit("teleport_unit", ref_unit->get_location(), fc_->get_disp_context().units()));
528  } else {
529  // Possible TODO: Support recall list units?
530  }
531  }
532 
533  std::set<map_location> match_set;
534 
535  // See if the caller provided an override to with_border
536  with_border = cfg_["include_borders"].to_bool(with_border);
537 
538  if (cfg_.has_attribute("find_in")) {
539 
540  if (const game_data * gd = fc_->get_game_data()) {
541  try
542  {
543  auto ar = gd->get_variable_access_read(cfg_["find_in"]).as_array();
544  terrain_filterimpl::filter_xy(ar | boost::adaptors::transformed(cfg_to_loc()), match_set, *this, with_border);
545  }
546  catch (const invalid_variablename_exception&)
547  {
548  //Do nothing
549  }
550  }
551  }
552  else if (cfg_.has_attribute("x") || cfg_.has_attribute("y")) {
553  std::vector<map_location> xy_vector = fc_->get_disp_context().map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
554  terrain_filterimpl::filter_area(xy_vector, match_set, *this, terrain_filterimpl::no_filter());
555  }
556  else if (cfg_.has_attribute("area")) {
557  const std::set<map_location>& area = fc_->get_tod_man().get_area_by_id(cfg_["area"]);
559  }
560  else if (cfg_.has_attribute("location_id")) {
561  for(const auto& id : utils::split(cfg_["location_id"])) {
562  map_location test_loc = fc_->get_disp_context().map().special_location(id);
563  if(test_loc.valid()) {
564  match_set.insert(test_loc);
565  }
566  }
567  }
568  else {
569  //consider all locations on the map
570  int bs = fc_->get_disp_context().map().border_size();
571  int w = with_border ? fc_->get_disp_context().map().w() + bs : fc_->get_disp_context().map().w();
572  int h = with_border ? fc_->get_disp_context().map().h() + bs : fc_->get_disp_context().map().h();
573  for (int x = with_border ? 0 - bs : 0; x < w; ++x) {
574  for (int y = with_border ? 0 - bs : 0; y < h; ++y) {
575  match_set.insert(map_location(x, y));
576  }
577  }
578  }
579 
580  //handle location filter
581  if(cfg_.has_child("filter_adjacent_location")) {
582  if(cache_.adjacent_matches == nullptr) {
583  cache_.adjacent_matches.reset(new std::vector<std::set<map_location>>());
584  }
585  const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
586  for (unsigned i = 0; i < adj_cfgs.size(); ++i) {
587  std::set<map_location> adj_set;
588  /* GCC-3.3 doesn't like operator[] so use at(), which has the same result */
589  terrain_filter(adj_cfgs.at(i), *this).get_locations(adj_set, with_border);
590  cache_.adjacent_matches->push_back(adj_set);
591  if(i >= max_loop_ && i+1 < adj_cfgs.size()) {
592  ERR_NG << "terrain_filter: loop count greater than " << max_loop_
593  << ", aborting\n";
594  break;
595  }
596  }
597  }
598  std::set<map_location>::iterator loc_itor = match_set.begin();
599  while(loc_itor != match_set.end()) {
600  if(match_internal(*loc_itor, ref_unit, true)) {
601  ++loc_itor;
602  } else {
603  loc_itor = match_set.erase(loc_itor);
604  }
605  }
606 
607  //handle [and], [or], and [not] with in-order precedence
608  vconfig::all_children_iterator cond = cfg_.ordered_begin();
609  vconfig::all_children_iterator cond_end = cfg_.ordered_end();
610  int ors_left = std::count_if(cond, cond_end, cfg_isor());
611  while(cond != cond_end)
612  {
613  //if there are no locations or [or] conditions left, go ahead and return empty
614  if(match_set.empty() && ors_left <= 0) {
615  return;
616  }
617 
618  const std::string& cond_name = cond.get_key();
619  const vconfig& cond_cfg = cond.get_child();
620 
621  //handle [and]
622  if(cond_name == "and") {
623  std::set<map_location> intersect_hexes;
624  terrain_filter(cond_cfg, *this).get_locations(intersect_hexes, with_border);
625  std::set<map_location>::iterator intersect_itor = match_set.begin();
626  while(intersect_itor != match_set.end()) {
627  if(intersect_hexes.find(*intersect_itor) == intersect_hexes.end()) {
628  match_set.erase(*intersect_itor++);
629  } else {
630  ++intersect_itor;
631  }
632  }
633  }
634  //handle [or]
635  else if(cond_name == "or") {
636  std::set<map_location> union_hexes;
637  terrain_filter(cond_cfg, *this).get_locations(union_hexes, with_border);
638  //match_set.insert(union_hexes.begin(), union_hexes.end()); //doesn't compile on MSVC
639  std::set<map_location>::iterator insert_itor = union_hexes.begin();
640  while(insert_itor != union_hexes.end()) {
641  match_set.insert(*insert_itor++);
642  }
643  --ors_left;
644  }
645  //handle [not]
646  else if(cond_name == "not") {
647  std::set<map_location> removal_hexes;
648  terrain_filter(cond_cfg, *this).get_locations(removal_hexes, with_border);
649  std::set<map_location>::iterator erase_itor = removal_hexes.begin();
650  while(erase_itor != removal_hexes.end()) {
651  match_set.erase(*erase_itor++);
652  }
653  }
654  ++cond;
655  }
656  if(match_set.empty()) {
657  return;
658  }
659 
660  //handle radius
661  std::size_t radius = cfg_["radius"].to_size_t(0);
662  if(radius > max_loop_) {
663  ERR_NG << "terrain_filter: radius greater than " << max_loop_
664  << ", restricting\n";
665  radius = max_loop_;
666  }
667  if(radius > 0) {
668  std::vector<map_location> xy_vector (match_set.begin(), match_set.end());
669  if(cfg_.has_child("filter_radius")) {
670  terrain_filter r_filter(cfg_.child("filter_radius"), *this);
671  get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border, r_filter);
672  } else {
673  get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border);
674  }
675  } else {
676  locs.insert(match_set.begin(), match_set.end());
677  }
678 }
679 
681 {
682  return cfg_.get_config();
683 }
bool operator()(const map_location &) const
Definition: filter.cpp:461
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:474
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:465
map_location operator()(const config &cfg) const
Definition: filter.cpp:519
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:475
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:124
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:522
const filter_context * fc_
Definition: filter.hpp:79
config to_config() const
Definition: filter.cpp:680
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:364
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:363
filter_with_unit(const terrain_filter &filt, const unit &ref)
Definition: filter.cpp:366
~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:652
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:367
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:520
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:1266
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:661
#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:493
bool match_impl(const map_location &loc, const unit *ref_unit) const
Definition: filter.cpp:372
std::vector< vconfig > child_list
Definition: variable.hpp:78
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:314
#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:68
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:505
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:457