The Battle for Wesnoth  1.19.2+dev
filter.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
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_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"
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 
43 static lg::log_domain log_wml("wml");
44 #define ERR_WML LOG_STREAM(err, log_wml)
45 
47 {
48 }
49 
50 terrain_filter::terrain_filter(const vconfig& cfg, const filter_context * fc, const bool flat_tod) :
51  cfg_(cfg),
52  fc_(fc),
53  cache_(),
54  max_loop_(game_config::max_loop),
55  flat_(flat_tod)
56 {
57 }
58 
59 terrain_filter::terrain_filter(const vconfig& cfg, const terrain_filter& original) :
60  cfg_(cfg),
61  fc_(original.fc_),
62  cache_(),
63  max_loop_(original.max_loop_),
64  flat_(original.flat_)
65 {
66 }
67 
68 terrain_filter::terrain_filter(const terrain_filter& other) :
69  xy_pred(), // We should construct this too, since it has no datamembers
70  // use the default constructor.
71  cfg_(other.cfg_),
72  fc_(other.fc_),
73  cache_(),
74  max_loop_(other.max_loop_),
75  flat_(other.flat_)
76 {
77 }
78 
79 terrain_filter& terrain_filter::operator=(const terrain_filter& other)
80 {
81  // Use copy constructor to make sure we are coherent
82  if (this != &other) {
83  this->~terrain_filter();
84  new (this) terrain_filter(other) ;
85  }
86  return *this ;
87 }
88 
90  parsed_terrain(nullptr),
91  adjacent_matches(nullptr),
92  adjacent_match_cache(),
93  ufilter_()
94 {}
95 
96 bool terrain_filter::match_internal(const map_location& loc, const unit* ref_unit, const bool ignore_xy) const
97 {
98  if (!this->fc_->get_disp_context().map().on_board_with_border(loc)) {
99  return false;
100  }
101 
102  std::string lua_function = cfg_["lua_function"];
103  if (!lua_function.empty() && fc_->get_lua_kernel()) {
104  if (!fc_->get_lua_kernel()->run_filter(lua_function.c_str(), loc)) {
105  return false;
106  }
107  }
108 
109  //Filter Areas
110  if (cfg_.has_attribute("area") &&
111  fc_->get_tod_man().get_area_by_id(cfg_["area"]).count(loc) == 0)
112  return false;
113 
114  if(cfg_.has_attribute("gives_income") &&
115  cfg_["gives_income"].to_bool() != fc_->get_disp_context().map().is_village(loc))
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(std::string_view(cfg_["terrain"].str())));
121  }
122  if(!cache_.parsed_terrain->is_empty) {
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"])) {
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")) {
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  const auto adjacent = get_adjacent_tiles(loc);
210  const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
211  vconfig::child_list::const_iterator i, i_end, i_begin = adj_cfgs.begin();
212  for (i = i_begin, i_end = adj_cfgs.end(); i != i_end; ++i) {
213  int match_count = 0;
214  vconfig::child_list::difference_type index = i - i_begin;
215  std::vector<map_location::DIRECTION> dirs = (*i).has_attribute("adjacent")
217  std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
218  for (j = dirs.begin(); j != j_end; ++j) {
219  const map_location &adj = adjacent[*j];
220  if (fc_->get_disp_context().map().on_board(adj)) {
221  if(cache_.adjacent_matches == nullptr) {
222  while(index >= std::distance(cache_.adjacent_match_cache.begin(), cache_.adjacent_match_cache.end())) {
223  const vconfig& adj_cfg = adj_cfgs[cache_.adjacent_match_cache.size()];
224  std::pair<terrain_filter, std::map<map_location,bool>> amc_pair(
225  terrain_filter(adj_cfg, *this),
226  std::map<map_location,bool>());
227  cache_.adjacent_match_cache.push_back(amc_pair);
228  }
229  terrain_filter &amc_filter = cache_.adjacent_match_cache[index].first;
230  std::map<map_location,bool> &amc = cache_.adjacent_match_cache[index].second;
231  std::map<map_location,bool>::iterator lookup = amc.find(adj);
232  if(lookup == amc.end()) {
233  if(amc_filter(adj)) {
234  amc[adj] = true;
235  ++match_count;
236  } else {
237  amc[adj] = false;
238  }
239  } else if(lookup->second) {
240  ++match_count;
241  }
242  } else {
243  assert(index < std::distance(cache_.adjacent_matches->begin(), cache_.adjacent_matches->end()));
244  std::set<map_location> &amc = (*cache_.adjacent_matches)[index];
245  if(amc.find(adj) != amc.end()) {
246  ++match_count;
247  }
248  }
249  }
250  }
251  static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges_unsigned("1-6");
252  std::vector<std::pair<int,int>> counts = (*i).has_attribute("count")
254  if(!in_ranges(match_count, counts)) {
255  return false;
256  }
257  }
258  }
259 
260  const t_string& t_tod_type = cfg_["time_of_day"];
261  const t_string& t_tod_id = cfg_["time_of_day_id"];
262  const std::string& tod_type = t_tod_type;
263  const std::string& tod_id = t_tod_id;
264  if(!tod_type.empty() || !tod_id.empty()) {
265  // creating a time_of_day is expensive, only do it if we will use it
266  time_of_day tod;
267 
268  if(flat_) {
269  tod = fc_->get_tod_man().get_time_of_day(loc);
270  } else {
272  }
273 
274  if(!tod_type.empty()) {
275  const std::vector<std::string>& vals = utils::split(tod_type);
276  if(tod.lawful_bonus<0) {
277  if(std::find(vals.begin(),vals.end(), unit_alignments::chaotic) == vals.end()) {
278  return false;
279  }
280  } else if(tod.lawful_bonus>0) {
281  if(std::find(vals.begin(),vals.end(), unit_alignments::lawful) == vals.end()) {
282  return false;
283  }
284  } else if(std::find(vals.begin(),vals.end(), unit_alignments::neutral) == vals.end() &&
285  std::find(vals.begin(),vals.end(), unit_alignments::liminal) == 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=";
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) == 0)
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_num = owner_side.to_int(0);
331  if(fc_->get_disp_context().village_owner(loc) != side_num) {
332  return false;
333  }
334  }
335 
336  if(cfg_.has_attribute("formula")) {
337  try {
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::log_to_chat() << "Formula error in location filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
353  ERR_WML << "Formula error in location filter: " << e.type << " at " << e.filename << ':' << e.line << ")";
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";
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  for(const auto& [key, filter] : cfg_.all_ordered()) {
416  // Handle [and]
417  if(key == "and") {
418  matches = matches && terrain_filter(filter, *this).match_impl(*i, ref_unit);
419  }
420  // Handle [or]
421  else if(key == "or") {
422  matches = matches || terrain_filter(filter, *this).match_impl(*i, ref_unit);
423  }
424  // Handle [not]
425  else if(key == "not") {
426  matches = matches && !terrain_filter(filter, *this).match_impl(*i, ref_unit);
427  }
428  }
429 
430  if(matches) {
431  return true;
432  }
433  if(++loop_count > max_loop_) {
434  std::set<map_location>::const_iterator temp = i;
435  if(++temp != hexes.end()) {
436  ERR_NG << "terrain_filter: loop count greater than " << max_loop_
437  << ", aborting";
438  break;
439  }
440  }
441  }
442  return false;
443 }
444 //using a class to be able to firen it in terrain_filter
446 {
447 public:
448  using location_set = std::set<map_location>;
449  struct no_start_set_yet {};
450  struct no_filter
451  {
452  bool operator()(const map_location&) const { return true; }
453  };
454 
455  template<typename T, typename F1, typename F2, typename F3>
456  static void filter_final(T&& src, location_set& dest, const terrain_filter&, const F1& f1, const F2& f2, const F3& f3)
457  {
458  for (map_location loc : src) {
459  if (f1(loc) && f2(loc) && f3(loc)) {
460  dest.insert(loc);
461  }
462  }
463  }
464 
465  template<typename T, typename F1, typename F2>
466  static void filter_special_loc(T&& src, location_set& dest, const terrain_filter& filter, const F1& f1, const F2& f2)
467  {
468  if (filter.cfg_.has_attribute("location_id")) {
469  std::set<map_location> matching_locs;
470  for(const auto& id : utils::split(filter.cfg_["location_id"])) {
471  map_location test_loc = filter.fc_->get_disp_context().map().special_location(id);
472  if(test_loc.valid()) {
473  matching_locs.insert(test_loc);
474  }
475  }
476  filter_final(src, dest, filter, f1, f2, [matching_locs](const map_location& loc) { return matching_locs.count(loc) > 0; });
477  }
478  else {
479  filter_final(src, dest, filter, f1, f2, no_filter());
480  }
481  }
482 
483  template<typename T, typename F1>
484  static void filter_area(T&& src, location_set& dest, const terrain_filter& filter, const F1& f1)
485  {
486  if (filter.cfg_.has_attribute("area")) {
487  const std::set<map_location>& area = filter.fc_->get_tod_man().get_area_by_id(filter.cfg_["area"]);
488  filter_special_loc(src, dest, filter, f1, [&area](const map_location& loc) { return area.find(loc) != area.end(); });
489  }
490  else {
491  filter_special_loc(src, dest, filter, f1, no_filter());
492  }
493  }
494 
495  template<typename T>
496  static void filter_xy(T&& src, location_set& dest, const terrain_filter& filter, bool with_border)
497  {
498  if (filter.cfg_.has_attribute("x") || filter.cfg_.has_attribute("y")) {
499  std::vector<map_location> xy_vector = filter.fc_->get_disp_context().map().parse_location_range(filter.cfg_["x"], filter.cfg_["y"], with_border);
500  filter_area(src, dest, filter, [&xy_vector](const map_location& loc) { return std::find(xy_vector.begin(), xy_vector.end(), loc) != xy_vector.end(); });
501  }
502  else {
503  filter_area(src, dest, filter, no_filter());
504  }
505  }
506 };
507 //using lambdas with boost transformed gives compile erros on gcc (it works on clang and msvc)
509 {
510  map_location operator()(const config& cfg) const { return map_location(cfg, nullptr); }
512 };
513 void terrain_filter::get_locs_impl(std::set<map_location>& locs, const unit* ref_unit, bool with_border) const
514 {
515  std::unique_ptr<scoped_wml_variable> ref_unit_var;
516  if(ref_unit) {
517  if(fc_->get_disp_context().map().on_board(ref_unit->get_location())) {
518  ref_unit_var.reset(new scoped_xy_unit("teleport_unit", ref_unit->get_location(), fc_->get_disp_context().units()));
519  } else {
520  // Possible TODO: Support recall list units?
521  }
522  }
523 
524  std::set<map_location> match_set;
525 
526  // See if the caller provided an override to with_border
527  with_border = cfg_["include_borders"].to_bool(with_border);
528 
529  if (cfg_.has_attribute("find_in")) {
530 
531  if (const game_data * gd = fc_->get_game_data()) {
532  try
533  {
534  auto ar = gd->get_variable_access_read(cfg_["find_in"]).as_array();
535  terrain_filterimpl::filter_xy(ar | boost::adaptors::transformed(cfg_to_loc()), match_set, *this, with_border);
536  }
537  catch (const invalid_variablename_exception&)
538  {
539  //Do nothing
540  }
541  }
542  }
543  else if (cfg_.has_attribute("x") || cfg_.has_attribute("y")) {
544  std::vector<map_location> xy_vector = fc_->get_disp_context().map().parse_location_range(cfg_["x"], cfg_["y"], with_border);
545  terrain_filterimpl::filter_area(xy_vector, match_set, *this, terrain_filterimpl::no_filter());
546  }
547  else if (cfg_.has_attribute("area")) {
548  const std::set<map_location>& area = fc_->get_tod_man().get_area_by_id(cfg_["area"]);
550  }
551  else if (cfg_.has_attribute("location_id")) {
552  for(const auto& id : utils::split(cfg_["location_id"])) {
554  if(test_loc.valid()) {
555  match_set.insert(test_loc);
556  }
557  }
558  }
559  else if (cfg_["gives_income"].to_bool()) {
560  auto ar = fc_->get_disp_context().map().villages();
561  terrain_filterimpl::filter_xy(ar, match_set, *this, with_border);
562  }
563  else {
564  //consider all locations on the map
565  int bs = fc_->get_disp_context().map().border_size();
566  int w = with_border ? fc_->get_disp_context().map().w() + bs : fc_->get_disp_context().map().w();
567  int h = with_border ? fc_->get_disp_context().map().h() + bs : fc_->get_disp_context().map().h();
568  for (int x = with_border ? 0 - bs : 0; x < w; ++x) {
569  for (int y = with_border ? 0 - bs : 0; y < h; ++y) {
570  match_set.insert(map_location(x, y));
571  }
572  }
573  }
574 
575  //handle location filter
576  if(cfg_.has_child("filter_adjacent_location")) {
577  if(cache_.adjacent_matches == nullptr) {
578  cache_.adjacent_matches.reset(new std::vector<std::set<map_location>>());
579  }
580  const vconfig::child_list& adj_cfgs = cfg_.get_children("filter_adjacent_location");
581  for (unsigned i = 0; i < adj_cfgs.size(); ++i) {
582  std::set<map_location> adj_set;
583  /* GCC-3.3 doesn't like operator[] so use at(), which has the same result */
584  terrain_filter(adj_cfgs.at(i), *this).get_locations(adj_set, with_border);
585  cache_.adjacent_matches->push_back(adj_set);
586  if(i >= max_loop_ && i+1 < adj_cfgs.size()) {
587  ERR_NG << "terrain_filter: loop count greater than " << max_loop_
588  << ", aborting";
589  break;
590  }
591  }
592  }
593  std::set<map_location>::iterator loc_itor = match_set.begin();
594  while(loc_itor != match_set.end()) {
595  if(match_internal(*loc_itor, ref_unit, true)) {
596  ++loc_itor;
597  } else {
598  loc_itor = match_set.erase(loc_itor);
599  }
600  }
601 
602  int ors_left = std::count_if(cfg_.ordered_begin(), cfg_.ordered_end(), [](const auto& val) { return val.first == "or"; });
603 
604  // Handle [and], [or], and [not] with in-order precedence
605  for(const auto& [key, filter] : cfg_.all_ordered()) {
606  //if there are no locations or [or] conditions left, go ahead and return empty
607  if(match_set.empty() && ors_left <= 0) {
608  return;
609  }
610 
611  // Handle [and]
612  if(key == "and") {
613  std::set<map_location> intersect_hexes;
614  terrain_filter(filter, *this).get_locations(intersect_hexes, with_border);
615  std::set<map_location>::iterator intersect_itor = match_set.begin();
616  while(intersect_itor != match_set.end()) {
617  if(intersect_hexes.find(*intersect_itor) == intersect_hexes.end()) {
618  match_set.erase(*intersect_itor++);
619  } else {
620  ++intersect_itor;
621  }
622  }
623  }
624  // Handle [or]
625  else if(key == "or") {
626  std::set<map_location> union_hexes;
627  terrain_filter(filter, *this).get_locations(union_hexes, with_border);
628  //match_set.insert(union_hexes.begin(), union_hexes.end()); //doesn't compile on MSVC
629  std::set<map_location>::iterator insert_itor = union_hexes.begin();
630  while(insert_itor != union_hexes.end()) {
631  match_set.insert(*insert_itor++);
632  }
633  --ors_left;
634  }
635  // Handle [not]
636  else if(key == "not") {
637  std::set<map_location> removal_hexes;
638  terrain_filter(filter, *this).get_locations(removal_hexes, with_border);
639  std::set<map_location>::iterator erase_itor = removal_hexes.begin();
640  while(erase_itor != removal_hexes.end()) {
641  match_set.erase(*erase_itor++);
642  }
643  }
644  }
645  if(match_set.empty()) {
646  return;
647  }
648 
649  //handle radius
650  std::size_t radius = cfg_["radius"].to_size_t(0);
651  if(radius > max_loop_) {
652  ERR_NG << "terrain_filter: radius greater than " << max_loop_
653  << ", restricting";
654  radius = max_loop_;
655  }
656  if(radius > 0) {
657  std::vector<map_location> xy_vector (match_set.begin(), match_set.end());
658  if(cfg_.has_child("filter_radius")) {
659  terrain_filter r_filter(cfg_.child("filter_radius"), *this);
660  get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border, r_filter);
661  } else {
662  get_tiles_radius(fc_->get_disp_context().map(), xy_vector, radius, locs, with_border);
663  }
664  } else {
665  locs.insert(match_set.begin(), match_set.end());
666  }
667 }
668 
670 {
671  return cfg_.get_config();
672 }
Variant for storing WML attributes.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
const team & get_team(int side) const
This getter takes a 1-based side number, not a 0-based team number.
int village_owner(const map_location &loc) const
Given the location of a village, will return the 1-based number of the team that currently owns it,...
virtual const gamemap & map() const =0
virtual const unit_map & units() const =0
virtual const display_context & get_disp_context() const =0
virtual game_lua_kernel * get_lua_kernel() const =0
virtual const tod_manager & get_tod_man() const =0
virtual const game_data * get_game_data() const =0
virtual bool matches(const gamemap_base &m, map_location l) const =0
const unit & ref_
Definition: filter.cpp:364
filter_with_unit(const terrain_filter &filt, const unit &ref)
Definition: filter.cpp:366
const terrain_filter & filt_
Definition: filter.cpp:363
bool operator()(const map_location &loc) const override
Definition: filter.cpp:367
bool run_filter(char const *name, const unit &u)
Runs a script from a unit filter.
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
map_location special_location(const std::string &id) const
Definition: map.cpp:311
std::vector< map_location > parse_location_range(const std::string &xvals, const std::string &yvals, bool with_border=false) const
Parses ranges of locations into a vector of locations, using this map's dimensions as bounds.
Definition: map.cpp:423
bool on_board_with_border(const map_location &loc) const
Definition: map.cpp:389
int border_size() const
Size of the map border.
Definition: map.hpp:56
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:384
bool is_village(const map_location &loc) const
Definition: map.cpp:65
const std::vector< map_location > & villages() const
Return a list of the locations of villages on the map.
Definition: map.hpp:237
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:97
std::vector< int > get_teams() const
Definition: side_filter.cpp:59
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
bool owns_village(const map_location &loc) const
Definition: team.hpp:171
bool shrouded(const map_location &loc) const
Definition: team.cpp:650
bool fogged(const map_location &loc) const
Definition: team.cpp:659
config to_config() const
Definition: filter.cpp:669
bool match_internal(const map_location &loc, const unit *ref_unit, const bool ignore_xy) const
Definition: filter.cpp:96
~terrain_filter()
Default implementation, but defined out-of-line for efficiency reasons.
Definition: filter.cpp:46
bool match_impl(const map_location &loc, const unit *ref_unit) const
Definition: filter.cpp:372
terrain_filter(const vconfig &cfg, const filter_context *fc, const bool flat_tod)
Definition: filter.cpp:50
terrain_filter & operator=(const terrain_filter &other)
Definition: filter.cpp:79
terrain_filter_cache cache_
Definition: filter.hpp:105
const vconfig cfg_
Definition: filter.hpp:87
const std::size_t max_loop_
Definition: filter.hpp:106
const filter_context * fc_
Definition: filter.hpp:88
void get_locs_impl(std::set< map_location > &locs, const unit *ref_unit, bool with_border) const
Definition: filter.cpp:513
static void filter_xy(T &&src, location_set &dest, const terrain_filter &filter, bool with_border)
Definition: filter.cpp:496
static void filter_area(T &&src, location_set &dest, const terrain_filter &filter, const F1 &f1)
Definition: filter.cpp:484
std::set< map_location > location_set
Definition: filter.cpp:448
static void filter_special_loc(T &&src, location_set &dest, const terrain_filter &filter, const F1 &f1, const F2 &f2)
Definition: filter.cpp:466
static void filter_final(T &&src, location_set &dest, const terrain_filter &, const F1 &f1, const F2 &f2, const F3 &f3)
Definition: filter.cpp:456
t_translation::terrain_code number() const
Definition: terrain.hpp:66
const time_of_day get_illuminated_time_of_day(const unit_map &units, const gamemap &map, const map_location &loc, int for_turn=0) const
Returns time of day object for the passed turn at a location.
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:56
const std::set< map_location > & get_area_by_id(const std::string &id) const
unit_iterator find(std::size_t id)
Definition: map.cpp:302
This class represents a single unit of a specific type.
Definition: unit.hpp:133
Information on a WML variable.
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...
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
all_children_iterator ordered_begin() const
In-order iteration over all children.
Definition: variable.cpp:477
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition: variable.hpp:99
std::vector< vconfig > child_list
Definition: variable.hpp:78
bool null() const
Definition: variable.hpp:72
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition: variable.cpp:288
const config & get_config() const
Definition: variable.hpp:75
all_children_iterator ordered_end() const
Definition: variable.cpp:482
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition: variable.cpp:163
boost::iterator_range< all_children_iterator > all_ordered() const
Definition: variable.hpp:190
child_list get_children(const std::string &key) const
Definition: variable.cpp:226
bool has_child(const std::string &key) const
Returns whether or not *this has a child whose key is key.
Definition: variable.cpp:315
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
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:253
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:313
std::size_t i
Definition: function.cpp:968
int w
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1389
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
Standard logging facilities (interface).
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp >> &ranges)
Definition: math.hpp:87
Game configuration data as global variables.
Definition: build_info.cpp:61
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:71
std::vector< std::pair< int, int > > default_counts
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:554
std::function< int(lua_State *)> lua_function
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::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:70
std::vector< std::pair< int, int > > parse_ranges_unsigned(const std::string &str)
Handles a comma-separated list of inputs to parse_range, in a context that does not expect negative v...
std::vector< std::string > split(const config_attribute_value &val)
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
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
int main(int, char **)
Definition: sdl2.cpp:25
map_location result_type
Definition: filter.cpp:511
map_location operator()(const config &cfg) const
Definition: filter.cpp:510
Encapsulates the map of the game.
Definition: location.hpp:38
static const std::vector< DIRECTION > & default_dirs()
Default list of directions.
Definition: location.cpp:52
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
bool valid() const
Definition: location.hpp:89
bool matches_range(const std::string &xloc, const std::string &yloc) const
Definition: location.cpp:317
This structure can be used for matching terrain strings.
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
std::unique_ptr< unit_filter > ufilter_
Definition: filter.hpp:102
std::vector< std::pair< terrain_filter, std::map< map_location, bool > > > adjacent_match_cache
Definition: filter.hpp:100
std::unique_ptr< t_translation::ter_match > parsed_terrain
Definition: filter.hpp:94
std::unique_ptr< std::vector< std::set< map_location > > > adjacent_matches
Definition: filter.hpp:97
bool operator()(const map_location &) const
Definition: filter.cpp:452
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
std::string id
Definition: time_of_day.hpp:90
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:83
bool valid() const
Definition: map.hpp:273
#define WRN_NG
Definition: filter.cpp:41
static lg::log_domain log_engine("engine")
#define ERR_NG
Definition: filter.cpp:40
#define ERR_WML
Definition: filter.cpp:44
static lg::log_domain log_wml("wml")
#define e
#define h