The Battle for Wesnoth  1.17.0-dev
filter.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
18 #include "config.hpp"
19 #include "display_context.hpp"
20 #include "filter_context.hpp"
21 #include "game_board.hpp"
22 #include "game_data.hpp"
23 #include "log.hpp"
24 #include "map/map.hpp"
25 #include "side_filter.hpp"
26 #include "team.hpp"
27 #include "terrain/filter.hpp"
28 #include "tod_manager.hpp"
29 #include "units/unit.hpp"
30 #include "units/filter.hpp"
31 #include "units/alignment.hpp"
32 #include "variable.hpp"
33 #include "formula/callable_objects.hpp"
34 #include "formula/formula.hpp"
37 
38 #include <boost/range/adaptor/transformed.hpp>
39 
40 static lg::log_domain log_engine("engine");
41 #define ERR_NG LOG_STREAM(err, log_engine)
42 #define WRN_NG LOG_STREAM(warn, log_engine)
43 
44 static lg::log_domain log_wml("wml");
45 #define ERR_WML LOG_STREAM(err, log_wml)
46 
48 {
49 }
50 
51 terrain_filter::terrain_filter(const vconfig& cfg, const filter_context * fc, const bool flat_tod) :
52  cfg_(cfg),
53  fc_(fc),
54  cache_(),
55  max_loop_(game_config::max_loop),
56  flat_(flat_tod)
57 {
58 }
59 
60 terrain_filter::terrain_filter(const vconfig& cfg, const terrain_filter& original) :
61  cfg_(cfg),
62  fc_(original.fc_),
63  cache_(),
64  max_loop_(original.max_loop_),
65  flat_(original.flat_)
66 {
67 }
68 
69 terrain_filter::terrain_filter(const terrain_filter& other) :
70  xy_pred(), // We should construct this too, since it has no datamembers
71  // use the default constructor.
72  cfg_(other.cfg_),
73  fc_(other.fc_),
74  cache_(),
75  max_loop_(other.max_loop_),
76  flat_(other.flat_)
77 {
78 }
79 
80 terrain_filter& terrain_filter::operator=(const terrain_filter& other)
81 {
82  // Use copy constructor to make sure we are coherent
83  if (this != &other) {
84  this->~terrain_filter();
85  new (this) terrain_filter(other) ;
86  }
87  return *this ;
88 }
89 
91  parsed_terrain(nullptr),
92  adjacent_matches(nullptr),
93  adjacent_match_cache(),
94  ufilter_()
95 {}
96 
97 bool terrain_filter::match_internal(const map_location& loc, const unit* ref_unit, const bool ignore_xy) const
98 {
99  if (!this->fc_->get_disp_context().map().on_board_with_border(loc)) {
100  return false;
101  }
102 
103  std::string lua_function = cfg_["lua_function"];
104  if (!lua_function.empty() && fc_->get_lua_kernel()) {
105  if (!fc_->get_lua_kernel()->run_filter(lua_function.c_str(), loc)) {
106  return false;
107  }
108  }
109 
110  //Filter Areas
111  if (cfg_.has_attribute("area") &&
112  fc_->get_tod_man().get_area_by_id(cfg_["area"]).count(loc) == 0)
113  return false;
114 
115  if(cfg_.has_attribute("gives_income") &&
116  cfg_["gives_income"].to_bool() != fc_->get_disp_context().map().is_village(loc))
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(std::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  const auto adjacent = get_adjacent_tiles(loc);
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  const 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_ALIGNMENT::enum_to_string(UNIT_ALIGNMENT::CHAOTIC)) == vals.end()) {
279  return false;
280  }
281  } else if(tod.lawful_bonus>0) {
282  if(std::find(vals.begin(),vals.end(),UNIT_ALIGNMENT::enum_to_string(UNIT_ALIGNMENT::LAWFUL)) == vals.end()) {
283  return false;
284  }
285  } else if(std::find(vals.begin(),vals.end(),UNIT_ALIGNMENT::enum_to_string(UNIT_ALIGNMENT::NEUTRAL)) == vals.end() &&
286  std::find(vals.begin(),vals.end(),UNIT_ALIGNMENT::enum_to_string(UNIT_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) == 0)
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_num = owner_side.to_int(0);
332  if(fc_->get_disp_context().village_owner(loc) != side_num) {
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::log_to_chat() << "Formula error in location filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
354  ERR_WML << "Formula error in location filter: " << e.type << " at " << e.filename << ':' << e.line << ")";
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  for(const auto& [key, filter] : cfg_.all_ordered()) {
417  // Handle [and]
418  if(key == "and") {
419  matches = matches && terrain_filter(filter, *this).match_impl(*i, ref_unit);
420  }
421  // Handle [or]
422  else if(key == "or") {
423  matches = matches || terrain_filter(filter, *this).match_impl(*i, ref_unit);
424  }
425  // Handle [not]
426  else if(key == "not") {
427  matches = matches && !terrain_filter(filter, *this).match_impl(*i, ref_unit);
428  }
429  }
430 
431  if(matches) {
432  return true;
433  }
434  if(++loop_count > max_loop_) {
435  std::set<map_location>::const_iterator temp = i;
436  if(++temp != hexes.end()) {
437  ERR_NG << "terrain_filter: loop count greater than " << max_loop_
438  << ", aborting\n";
439  break;
440  }
441  }
442  }
443  return false;
444 }
445 //using a class to be able to firen it in terrain_filter
447 {
448 public:
449  using location_set = std::set<map_location>;
450  struct no_start_set_yet {};
451  struct no_filter
452  {
453  bool operator()(const map_location&) const { return true; }
454  };
455 
456  template<typename T, typename F1, typename F2, typename F3>
457  static void filter_final(T&& src, location_set& dest, const terrain_filter&, const F1& f1, const F2& f2, const F3& f3)
458  {
459  for (map_location loc : src) {
460  if (f1(loc) && f2(loc) && f3(loc)) {
461  dest.insert(loc);
462  }
463  }
464  }
465 
466  template<typename T, typename F1, typename F2>
467  static void filter_special_loc(T&& src, location_set& dest, const terrain_filter& filter, const F1& f1, const F2& f2)
468  {
469  if (filter.cfg_.has_attribute("location_id")) {
470  std::set<map_location> matching_locs;
471  for(const auto& id : utils::split(filter.cfg_["location_id"])) {
472  map_location test_loc = filter.fc_->get_disp_context().map().special_location(id);
473  if(test_loc.valid()) {
474  matching_locs.insert(test_loc);
475  }
476  }
477  filter_final(src, dest, filter, f1, f2, [matching_locs](const map_location& loc) { return matching_locs.count(loc) > 0; });
478  }
479  else {
480  filter_final(src, dest, filter, f1, f2, no_filter());
481  }
482  }
483 
484  template<typename T, typename F1>
485  static void filter_area(T&& src, location_set& dest, const terrain_filter& filter, const F1& f1)
486  {
487  if (filter.cfg_.has_attribute("area")) {
488  const std::set<map_location>& area = filter.fc_->get_tod_man().get_area_by_id(filter.cfg_["area"]);
489  filter_special_loc(src, dest, filter, f1, [&area](const map_location& loc) { return area.find(loc) != area.end(); });
490  }
491  else {
492  filter_special_loc(src, dest, filter, f1, no_filter());
493  }
494  }
495 
496  template<typename T>
497  static void filter_xy(T&& src, location_set& dest, const terrain_filter& filter, bool with_border)
498  {
499  if (filter.cfg_.has_attribute("x") || filter.cfg_.has_attribute("y")) {
500  std::vector<map_location> xy_vector = filter.fc_->get_disp_context().map().parse_location_range(filter.cfg_["x"], filter.cfg_["y"], with_border);
501  filter_area(src, dest, filter, [&xy_vector](const map_location& loc) { return std::find(xy_vector.begin(), xy_vector.end(), loc) != xy_vector.end(); });
502  }
503  else {
504  filter_area(src, dest, filter, no_filter());
505  }
506  }
507 };
508 //using lambdas with boost transformed gives compile erros on gcc (it works on clang and msvc)
510 {
511  map_location operator()(const config& cfg) const { return map_location(cfg, nullptr); }
513 };
514 void terrain_filter::get_locs_impl(std::set<map_location>& locs, const unit* ref_unit, bool with_border) const
515 {
516  std::unique_ptr<scoped_wml_variable> ref_unit_var;
517  if(ref_unit) {
518  if(fc_->get_disp_context().map().on_board(ref_unit->get_location())) {
519  ref_unit_var.reset(new scoped_xy_unit("teleport_unit", ref_unit->get_location(), fc_->get_disp_context().units()));
520  } else {
521  // Possible TODO: Support recall list units?
522  }
523  }
524 
525  std::set<map_location> match_set;
526 
527  // See if the caller provided an override to with_border
528  with_border = cfg_["include_borders"].to_bool(with_border);
529 
530  if (cfg_.has_attribute("find_in")) {
531 
532  if (const game_data * gd = fc_->get_game_data()) {
533  try
534  {
535  auto ar = gd->get_variable_access_read(cfg_["find_in"]).as_array();
536  terrain_filterimpl::filter_xy(ar | boost::adaptors::transformed(cfg_to_loc()), match_set, *this, with_border);
537  }
538  catch (const invalid_variablename_exception&)
539  {
540  //Do nothing
541  }
542  }
543  }
544  else if (cfg_.has_attribute("x") || cfg_.has_attribute("y")) {
545  std::vector<map_location> xy_vector = fc_->get_disp_context().map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
546  terrain_filterimpl::filter_area(xy_vector, match_set, *this, terrain_filterimpl::no_filter());
547  }
548  else if (cfg_.has_attribute("area")) {
549  const std::set<map_location>& area = fc_->get_tod_man().get_area_by_id(cfg_["area"]);
551  }
552  else if (cfg_.has_attribute("location_id")) {
553  for(const auto& id : utils::split(cfg_["location_id"])) {
554  map_location test_loc = fc_->get_disp_context().map().special_location(id);
555  if(test_loc.valid()) {
556  match_set.insert(test_loc);
557  }
558  }
559  }
560  else if (cfg_["gives_income"].to_bool()) {
561  auto ar = fc_->get_disp_context().map().villages();
562  terrain_filterimpl::filter_xy(ar, match_set, *this, with_border);
563  }
564  else {
565  //consider all locations on the map
566  int bs = fc_->get_disp_context().map().border_size();
567  int w = with_border ? fc_->get_disp_context().map().w() + bs : fc_->get_disp_context().map().w();
568  int h = with_border ? fc_->get_disp_context().map().h() + bs : fc_->get_disp_context().map().h();
569  for (int x = with_border ? 0 - bs : 0; x < w; ++x) {
570  for (int y = with_border ? 0 - bs : 0; y < h; ++y) {
571  match_set.insert(map_location(x, y));
572  }
573  }
574  }
575 
576  //handle location filter
577  if(cfg_.has_child("filter_adjacent_location")) {
578  if(cache_.adjacent_matches == nullptr) {
579  cache_.adjacent_matches.reset(new std::vector<std::set<map_location>>());
580  }
581  const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
582  for (unsigned i = 0; i < adj_cfgs.size(); ++i) {
583  std::set<map_location> adj_set;
584  /* GCC-3.3 doesn't like operator[] so use at(), which has the same result */
585  terrain_filter(adj_cfgs.at(i), *this).get_locations(adj_set, with_border);
586  cache_.adjacent_matches->push_back(adj_set);
587  if(i >= max_loop_ && i+1 < adj_cfgs.size()) {
588  ERR_NG << "terrain_filter: loop count greater than " << max_loop_
589  << ", aborting\n";
590  break;
591  }
592  }
593  }
594  std::set<map_location>::iterator loc_itor = match_set.begin();
595  while(loc_itor != match_set.end()) {
596  if(match_internal(*loc_itor, ref_unit, true)) {
597  ++loc_itor;
598  } else {
599  loc_itor = match_set.erase(loc_itor);
600  }
601  }
602 
603  int ors_left = std::count_if(cfg_.ordered_begin(), cfg_.ordered_end(), [](const auto& val) { return val.first == "or"; });
604 
605  // Handle [and], [or], and [not] with in-order precedence
606  for(const auto& [key, filter] : cfg_.all_ordered()) {
607  //if there are no locations or [or] conditions left, go ahead and return empty
608  if(match_set.empty() && ors_left <= 0) {
609  return;
610  }
611 
612  // Handle [and]
613  if(key == "and") {
614  std::set<map_location> intersect_hexes;
615  terrain_filter(filter, *this).get_locations(intersect_hexes, with_border);
616  std::set<map_location>::iterator intersect_itor = match_set.begin();
617  while(intersect_itor != match_set.end()) {
618  if(intersect_hexes.find(*intersect_itor) == intersect_hexes.end()) {
619  match_set.erase(*intersect_itor++);
620  } else {
621  ++intersect_itor;
622  }
623  }
624  }
625  // Handle [or]
626  else if(key == "or") {
627  std::set<map_location> union_hexes;
628  terrain_filter(filter, *this).get_locations(union_hexes, with_border);
629  //match_set.insert(union_hexes.begin(), union_hexes.end()); //doesn't compile on MSVC
630  std::set<map_location>::iterator insert_itor = union_hexes.begin();
631  while(insert_itor != union_hexes.end()) {
632  match_set.insert(*insert_itor++);
633  }
634  --ors_left;
635  }
636  // Handle [not]
637  else if(key == "not") {
638  std::set<map_location> removal_hexes;
639  terrain_filter(filter, *this).get_locations(removal_hexes, with_border);
640  std::set<map_location>::iterator erase_itor = removal_hexes.begin();
641  while(erase_itor != removal_hexes.end()) {
642  match_set.erase(*erase_itor++);
643  }
644  }
645  }
646  if(match_set.empty()) {
647  return;
648  }
649 
650  //handle radius
651  std::size_t radius = cfg_["radius"].to_size_t(0);
652  if(radius > max_loop_) {
653  ERR_NG << "terrain_filter: radius greater than " << max_loop_
654  << ", restricting\n";
655  radius = max_loop_;
656  }
657  if(radius > 0) {
658  std::vector<map_location> xy_vector (match_set.begin(), match_set.end());
659  if(cfg_.has_child("filter_radius")) {
660  terrain_filter r_filter(cfg_.child("filter_radius"), *this);
661  get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border, r_filter);
662  } else {
663  get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border);
664  }
665  } else {
666  locs.insert(match_set.begin(), match_set.end());
667  }
668 }
669 
671 {
672  return cfg_.get_config();
673 }
bool operator()(const map_location &) const
Definition: filter.cpp:453
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:475
This class represents a single unit of a specific type.
Definition: unit.hpp:121
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:40
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:108
static void filter_final(T &&src, location_set &dest, const terrain_filter &, const F1 &f1, const F2 &f2, const F3 &f3)
Definition: filter.cpp:457
static lg::log_domain log_wml("wml")
map_location operator()(const config &cfg) const
Definition: filter.cpp:511
std::string id
Definition: time_of_day.hpp:90
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:83
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:87
#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:467
terrain_filter_cache cache_
Definition: filter.hpp:105
#define WRN_NG
Definition: filter.cpp:42
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:125
const vconfig cfg_
Definition: filter.hpp:87
#define ERR_WML
Definition: filter.cpp:45
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:93
Definitions for the interface to Wesnoth Markup Language (WML).
bool match_internal(const map_location &loc, const unit *ref_unit, const bool ignore_xy) const
Definition: filter.cpp:97
int main(int argc, char **argv)
Definition: SDLMain.mm:115
void get_locs_impl(std::set< map_location > &locs, const unit *ref_unit, bool with_border) const
Definition: filter.cpp:514
const filter_context * fc_
Definition: filter.hpp:88
config to_config() const
Definition: filter.cpp:670
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:56
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
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:253
bool valid() const
Definition: location.hpp:89
std::string type
Definition: formula.hpp:106
static const std::vector< DIRECTION > & default_dirs()
Default list of directions.
Definition: location.cpp:53
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:47
Encapsulates the map of the game.
Definition: location.hpp:38
bool shrouded(const map_location &loc) const
Definition: team.cpp:652
std::size_t i
Definition: function.cpp:967
bool operator()(const map_location &loc) const override
Definition: filter.cpp:368
Game configuration data as global variables.
Definition: build_info.cpp:59
terrain_filter & operator=(const terrain_filter &other)
Definition: filter.cpp:80
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:70
map_location result_type
Definition: filter.cpp:512
int w
bool matches_range(const std::string &xloc, const std::string &yloc) const
Definition: location.cpp:318
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:72
Information on a WML variable.
std::vector< std::string > split(const config_attribute_value &val)
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1346
std::vector< int > get_teams() const
Definition: side_filter.cpp:61
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: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...
terrain_filter(const vconfig &cfg, const filter_context *fc, const bool flat_tod)
Definition: filter.cpp:51
static void filter_area(T &&src, location_set &dest, const terrain_filter &filter, const F1 &f1)
Definition: filter.cpp:485
bool match_impl(const map_location &loc, const unit *ref_unit) const
Definition: filter.cpp:373
std::vector< vconfig > child_list
Definition: variable.hpp:78
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:317
#define ERR_NG
Definition: filter.cpp:41
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
bool null() const
Definition: variable.hpp:72
bool valid() const
Definition: map.hpp:274
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:497
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
const std::size_t max_loop_
Definition: filter.hpp:106
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:289
std::set< map_location > location_set
Definition: filter.cpp:449