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