The Battle for Wesnoth  1.17.14+dev
filter.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2022
3  by Chris Beck <render787@gmail.com>
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 #include "units/filter.hpp"
17 
18 #include "log.hpp"
19 
20 #include "display.hpp"
21 #include "display_context.hpp"
22 #include "config.hpp"
23 #include "game_data.hpp"
24 #include "map/map.hpp"
25 #include "map/location.hpp"
26 #include "scripting/game_lua_kernel.hpp" //Needed for lua kernel
27 #include "side_filter.hpp"
28 #include "team.hpp"
29 #include "terrain/filter.hpp"
30 #include "tod_manager.hpp"
31 #include "units/unit.hpp"
33 #include "units/types.hpp"
34 #include "variable.hpp" // needed for vconfig, scoped unit
35 #include "formula/callable_objects.hpp"
36 #include "formula/formula.hpp"
38 #include "formula/string_utils.hpp"
39 #include "resources.hpp"
40 
41 static lg::log_domain log_config("config");
42 #define ERR_CF LOG_STREAM(err, log_config)
43 #define WRN_CF LOG_STREAM(warn, log_config)
44 #define DBG_CF LOG_STREAM(debug, log_config)
45 
46 static lg::log_domain log_wml("wml");
47 #define ERR_WML LOG_STREAM(err, log_wml)
48 
49 using namespace unit_filter_impl;
50 
52  : cfg_(cfg)
53  , fc_(resources::filter_con)
54  , use_flat_tod_(false)
55  , impl_(cfg_)
56  , max_matches_(-1)
57 {
58 }
59 
60 bool unit_filter::matches(const unit& u) const {
62 }
63 
64 bool unit_filter::matches(const unit & u, const unit & u2) const {
66 }
67 
68 std::vector<const unit *> unit_filter::all_matches_on_map(const map_location* loc, const unit* other_unit) const
69 {
70  std::vector<const unit *> ret;
71  int max_matches = max_matches_;
72 
73  for (const unit & u : fc_->get_disp_context().units()) {
74  if (impl_.matches(unit_filter_impl::unit_filter_args{u, loc ? *loc : u.get_location(), other_unit, fc_, use_flat_tod_})) {
75  if(max_matches == 0) {
76  return ret;
77  }
78  --max_matches;
79  ret.push_back(&u);
80  }
81  }
82  return ret;
83 }
84 
86  const unit_map & units = fc_->get_disp_context().units();
87  for(unit_map::const_iterator u = units.begin(); u != units.end(); ++u) {
88  if (matches(*u, u->get_location())) {
89  return u.get_shared_ptr();
90  }
91  }
92  return unit_const_ptr();
93 }
94 
95 namespace {
96 
97 struct unit_filter_xy : public unit_filter_base
98 {
99  unit_filter_xy(const std::string& x, const std::string& y) : x_(x), y_(y) {}
100 
101  virtual bool matches(const unit_filter_args& args) const override
102  {
105 
106  if(x.empty() && y.empty()) {
107  return false;
108  }
109  else if(x == "recall" && y == "recall") {
110  return !args.context().get_disp_context().map().on_board(args.loc);
111  }
112  else {
113  return args.loc.matches_range(x, y);
114  }
115  }
116 
117  const std::string x_;
118  const std::string y_;
119 };
120 
121 struct unit_filter_adjacent : public unit_filter_base
122 {
123  unit_filter_adjacent(const vconfig& cfg)
124  : child_(cfg)
125  , cfg_(cfg)
126  {
127  }
128 
129  virtual bool matches(const unit_filter_args& args) const override
130  {
131  const unit_map& units = args.context().get_disp_context().units();
132  const auto adjacent = get_adjacent_tiles(args.loc);
133  int match_count=0;
134 
135  config::attribute_value i_adjacent = cfg_["adjacent"];
136  std::vector<map_location::DIRECTION> dirs;
137  if (i_adjacent.empty()) {
139  } else {
140  dirs = map_location::parse_directions(i_adjacent);
141  }
142  for (map_location::DIRECTION dir : dirs) {
143  unit_map::const_iterator unit_itor = units.find(adjacent[dir]);
144  if (unit_itor == units.end() || !child_.matches(unit_filter_args{*unit_itor, unit_itor->get_location(), &args.u, args.fc, args.use_flat_tod} )) {
145  continue;
146  }
147  auto is_enemy = cfg_["is_enemy"];
148  if (!is_enemy.empty() && is_enemy.to_bool() != args.context().get_disp_context().get_team(args.u.side()).is_enemy(unit_itor->side())) {
149  continue;
150  }
151  ++match_count;
152  }
153 
154  static std::vector<std::pair<int,int>> default_counts = utils::parse_ranges("1-6");
155  config::attribute_value i_count = cfg_["count"];
156  return in_ranges(match_count, !i_count.blank() ? utils::parse_ranges(i_count) : default_counts);
157  }
158 
159  const unit_filter_compound child_;
160  const vconfig cfg_;
161 };
162 
163 
164 template<typename F>
165 struct unit_filter_child_literal : public unit_filter_base
166 {
167  unit_filter_child_literal(const vconfig& v, const F& f) : v_(v) , f_(f) {}
168  virtual bool matches(const unit_filter_args& args) const override
169  {
170  return f_(v_, args);
171  }
172  vconfig v_;
173  F f_;
174 };
175 
176 template<typename T, typename F>
177 struct unit_filter_attribute_parsed : public unit_filter_base
178 {
179  unit_filter_attribute_parsed(T&& v, F&& f) : v_(std::move(v)), f_(std::move(f)) {}
180  virtual bool matches(const unit_filter_args& args) const override
181  {
182  return f_(v_, args);
183  }
184  T v_;
185  F f_;
186 };
187 
188 template<typename C, typename F>
189 struct unit_filter_attribute_literal : public unit_filter_base
190 {
191  unit_filter_attribute_literal(std::string&& v, C&& c, F&& f) : v_(std::move(v)), c_(std::move(c)), f_(std::move(f)) {}
192  virtual bool matches(const unit_filter_args& args) const override
193  {
196  return f_(c_(v), args);
197  }
198  std::string v_;
199  C c_;
200  F f_;
201 };
202 
203 class contains_dollar_visitor
204 #ifdef USING_BOOST_VARIANT
205  : public boost::static_visitor<bool>
206 #endif
207 {
208 public:
209  contains_dollar_visitor() {}
210 
211 
212  template<typename T>
213  bool operator()(const T&) const { return false; }
214 
215  bool operator()(const t_string&) const { return true; }
216 
217  bool operator()(const std::string& s) const
218  {
219  return s.find('$') != std::string::npos;
220  }
221 };
222 
223 }
224 
225 
226 unit_filter_compound::unit_filter_compound(vconfig cfg)
227  : children_()
228  , cond_children_()
229 {
230  fill(cfg);
231 }
232 
234 {
235  bool res;
236 
237  if(args.loc.valid()) {
238  scoped_xy_unit auto_store("this_unit", args.u.get_location(), args.context().get_disp_context().units());
239  if (args.u2) {
240  const map_location& loc2 = args.u2->get_location();
241  scoped_xy_unit u2_auto_store("other_unit", loc2, args.context().get_disp_context().units());
242  res = filter_impl(args);
243  } else {
244  res = filter_impl(args);
245  }
246  } else {
247  // If loc is invalid, then this is a recall list unit (already been scoped)
248  res = filter_impl(args);
249  }
250 
251  // Handle [and], [or], and [not] with in-order precedence
252  for(const auto & filter : cond_children_) {
253  switch (filter.first) {
254  case conditional_type::type::filter_and:
255  res = res && filter.second.matches(args);
256  break;
257  case conditional_type::type::filter_or:
258  res = res || filter.second.matches(args);
259  break;
260  case conditional_type::type::filter_not:
261  res = res && !filter.second.matches(args);
262  break;
263  }
264  }
265  return res;
266 }
267 
269 {
270  for(const auto & filter : children_) {
271  if (!filter->matches(args)) {
272  return false;
273  }
274  }
275  return true;
276 }
277 
278 template<typename F>
280 {
281  children_.emplace_back(new unit_filter_child_literal<F>(c, func));
282 }
283 
284 template<typename C, typename F>
286 {
287  if(v.blank()) {
288  }
289  else if(v.apply_visitor(contains_dollar_visitor())) {
290  children_.emplace_back(new unit_filter_attribute_literal<C, F>(std::move(v.str()), std::move(conv), std::move(func)));
291  }
292  else {
293  children_.emplace_back(new unit_filter_attribute_parsed<decltype(conv(v)), F>(std::move(conv(v)), std::move(func)));
294  }
295 }
296 
297 namespace {
298 
299  struct ability_match
300  {
301  std::string tag_name;
302  const config* cfg;
303  };
304 
305  void get_ability_children_id(std::vector<ability_match>& id_result,
306  const config& parent, const std::string& id) {
307  for (const config::any_child sp : parent.all_children_range())
308  {
309  if(sp.cfg["id"] == id) {
310  ability_match special = { sp.key, &sp.cfg };
311  id_result.push_back(special);
312  }
313  }
314  }
315 }
316 
318  {
319  const config& literal = cfg.get_config();
320 
321  //optimisation
322  if(literal.empty()) { return; }
323 
324  create_attribute(literal["name"],
325  [](const config::attribute_value& c) { return c.t_str(); },
326  [](const t_string& str, const unit_filter_args& args) { return str == args.u.name(); }
327  );
328 
329  create_attribute(literal["id"],
330  [](const config::attribute_value& c) { return utils::split(c.str()); },
331  [](const std::vector<std::string>& id_list, const unit_filter_args& args)
332  {
333  return std::find(id_list.begin(), id_list.end(), args.u.id()) != id_list.end();
334  }
335  );
336 
337  create_attribute(literal["type"],
338  [](const config::attribute_value& c) { return utils::split(c.str()); },
339  [](const std::vector<std::string>& types, const unit_filter_args& args)
340  {
341  return std::find(types.begin(), types.end(), args.u.type_id()) != types.end();
342  }
343  );
344 
345  create_attribute(literal["type_adv_tree"],
346  [](const config::attribute_value& c) { return utils::split(c.str()); },
347  [](const std::vector<std::string>& types, const unit_filter_args& args)
348  {
349  std::set<std::string> types_expanded;
350  for(const std::string& type : types) {
351  if(types_expanded.count(type)) {
352  continue;
353  }
354  if(const unit_type* ut = unit_types.find(type)) {
355  const auto& tree = ut->advancement_tree();
356  types_expanded.insert(tree.begin(), tree.end());
357  types_expanded.insert(type);
358  }
359  }
360  return types_expanded.find(args.u.type_id()) != types_expanded.end();
361  }
362  );
363 
364  create_attribute(literal["variation"],
365  [](const config::attribute_value& c) { return utils::split(c.str()); },
366  [](const std::vector<std::string>& types, const unit_filter_args& args)
367  {
368  return std::find(types.begin(), types.end(), args.u.variation()) != types.end();
369  }
370  );
371 
372  create_attribute(literal["has_variation"],
373  [](const config::attribute_value& c) { return utils::split(c.str()); },
374  [](const std::vector<std::string>& types, const unit_filter_args& args)
375  {
376  // If this unit is a variation itself then search in the base unit's variations.
377  const unit_type* const type = args.u.variation().empty() ? &args.u.type() : unit_types.find(args.u.type().parent_id());
378  assert(type);
379 
380  for(const std::string& variation_id : types) {
381  if (type->has_variation(variation_id)) {
382  return true;
383  }
384  }
385  return false;
386  }
387  );
388 
389  create_attribute(literal["ability"],
390  [](const config::attribute_value& c) { return utils::split(c.str()); },
391  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
392  {
393  for(const std::string& ability_id : abilities) {
394  if (args.u.has_ability_by_id(ability_id)) {
395  return true;
396  }
397  }
398  return false;
399  }
400  );
401 
402  create_attribute(literal["ability_type"],
403  [](const config::attribute_value& c) { return utils::split(c.str()); },
404  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
405  {
406  for(const std::string& ability : abilities) {
407  if (args.u.has_ability_type(ability)) {
408  return true;
409  }
410  }
411  return false;
412  }
413  );
414 
415  create_attribute(literal["ability_id_active"],
416  [](const config::attribute_value& c) { return utils::split(c.str()); },
417  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
418  {
419  assert(display::get_singleton());
420  const unit_map& units = display::get_singleton()->get_units();
421  for(const std::string& ability : abilities) {
422  std::vector<ability_match> ability_id_matches_self;
423  get_ability_children_id(ability_id_matches_self, args.u.abilities(), ability);
424  for(const ability_match& entry : ability_id_matches_self) {
425  if (args.u.get_self_ability_bool(*entry.cfg, entry.tag_name, args.loc)) {
426  return true;
427  }
428  }
429 
430  const auto adjacent = get_adjacent_tiles(args.loc);
431  for(unsigned i = 0; i < adjacent.size(); ++i) {
432  const unit_map::const_iterator it = units.find(adjacent[i]);
433  if (it == units.end() || it->incapacitated())
434  continue;
435  if (&*it == (args.u.shared_from_this()).get())
436  continue;
437 
438  std::vector<ability_match> ability_id_matches_adj;
439  get_ability_children_id(ability_id_matches_adj, it->abilities(), ability);
440  for(const ability_match& entry : ability_id_matches_adj) {
441  if (args.u.get_adj_ability_bool(*entry.cfg, entry.tag_name,i, args.loc, *it)) {
442  return true;
443  }
444  }
445  }
446  }
447  return false;
448  }
449  );
450 
451  create_attribute(literal["ability_type_active"],
452  [](const config::attribute_value& c) { return utils::split(c.str()); },
453  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
454  {
455  for(const std::string& ability : abilities) {
456  if (!args.u.get_abilities(ability, args.loc).empty()) {
457  return true;
458  }
459  }
460  return false;
461  }
462  );
463 
464  create_attribute(literal["trait"],
465  [](const config::attribute_value& c)
466  {
467  auto res = utils::split(c.str());
468  std::sort(res.begin(), res.end());
469  return res;
470 
471  },
472  [](const std::vector<std::string>& check_traits, const unit_filter_args& args)
473  {
474 
475  std::vector<std::string> have_traits = args.u.get_traits_list();
476  std::vector<std::string> isect;
477  std::sort(have_traits.begin(), have_traits.end());
478  std::set_intersection(check_traits.begin(), check_traits.end(), have_traits.begin(), have_traits.end(), std::back_inserter(isect));
479  return !isect.empty();
480  }
481  );
482 
483  create_attribute(literal["race"],
484  [](const config::attribute_value& c) { return utils::split(c.str()); },
485  [](const std::vector<std::string>& races, const unit_filter_args& args)
486  {
487  return std::find(races.begin(), races.end(), args.u.race()->id()) != races.end();
488  }
489  );
490 
491  create_attribute(literal["gender"],
492  [](const config::attribute_value& c) { return string_gender(c.str()); },
493  [](unit_race::GENDER gender, const unit_filter_args& args)
494  {
495  return gender == args.u.gender();
496  }
497  );
498 
499  create_attribute(literal["upkeep"],
501  {
502  try {
504  } catch(std::invalid_argument&) {
505  return unit::upkeep_full();
506  }
507  },
508  [](unit::upkeep_t upkeep, const unit_filter_args& args)
509  {
510  return args.u.upkeep() == utils::visit(unit::upkeep_value_visitor{args.u}, upkeep);
511  }
512  );
513 
514  create_attribute(literal["side"],
515  [](const config::attribute_value& c)
516  {
517  std::vector<int> res;
518  for(const std::string& s : utils::split(c.str())) {
519  try {
520  res.push_back(std::stoi(s));
521  } catch(std::invalid_argument&) {
522  WRN_CF << "ignored invalid side='" << s << "' in filter";
523  }
524  }
525  return res;
526  },
527  [](const std::vector<int>& sides, const unit_filter_args& args)
528  {
529  return std::find(sides.begin(), sides.end(), args.u.side()) != sides.end();
530  }
531  );
532 
533  create_attribute(literal["status"],
534  [](const config::attribute_value& c) { return utils::split(c.str()); },
535  [](const std::vector<std::string>& statuses, const unit_filter_args& args)
536  {
537  for(const std::string& status : statuses) {
538  if (args.u.get_state(status)) {
539  return true;
540  }
541  }
542  return false;
543  }
544  );
545 
546  create_attribute(literal["has_weapon"],
547  [](const config::attribute_value& c) { return c.str(); },
548  [](const std::string& weapon, const unit_filter_args& args)
549  {
550 
551  for(const attack_type& a : args.u.attacks()) {
552  if(a.id() == weapon) {
553  return true;
554  }
555  }
556  return false;
557  }
558  );
559 
560  create_attribute(literal["role"],
561  [](const config::attribute_value& c) { return c.str(); },
562  [](const std::string& role, const unit_filter_args& args)
563  {
564  return args.u.get_role() == role;
565  }
566  );
567 
568  create_attribute(literal["alignment"],
569  [](const config::attribute_value& c) { return c.str(); },
570  [](const std::string& alignment, const unit_filter_args& args)
571  {
572  return unit_alignments::get_string(args.u.alignment()) == alignment;
573  }
574  );
575 
576  create_attribute(literal["ai_special"],
577  [](const config::attribute_value& c) { return c.str(); },
578  [](const std::string& ai_special, const unit_filter_args& args)
579  {
580  return (ai_special == "guardian") == args.u.get_state(unit::STATE_GUARDIAN);
581  }
582  );
583 
584  create_attribute(literal["usage"],
585  [](const config::attribute_value& c) { return utils::split(c.str()); },
586  [](const std::vector<std::string>& usages, const unit_filter_args& args)
587  {
588  for(const std::string& usage : usages) {
589  if(args.u.usage() == usage) {
590  return true;
591  }
592  }
593  return false;
594  }
595  );
596 
597  create_attribute(literal["canrecruit"],
598  [](const config::attribute_value& c) { return c.to_bool(); },
599  [](bool canrecruit, const unit_filter_args& args)
600  {
601  return args.u.can_recruit() == canrecruit;
602  }
603  );
604 
605  create_attribute(literal["recall_cost"],
606  [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
607  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
608  {
609  for(auto cost : ranges) {
610  if(cost.first <= args.u.recall_cost() && args.u.recall_cost() <= cost.second) {
611  return true;
612  }
613  }
614  return false;
615  }
616  );
617 
618  create_attribute(literal["level"],
619  [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
620  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
621  {
622  for(auto lvl : ranges) {
623  if(lvl.first <= args.u.level() && args.u.level() <= lvl.second) {
624  return true;
625  }
626  }
627  return false;
628  }
629  );
630 
631  create_attribute(literal["defense"],
632  [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
633  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
634  {
635  int actual_defense = args.u.defense_modifier(args.context().get_disp_context().map().get_terrain(args.loc));
636  for(auto def : ranges) {
637  if(def.first <= actual_defense && actual_defense <= def.second) {
638  return true;
639  }
640  }
641  return false;
642  }
643  );
644 
645  create_attribute(literal["movement_cost"],
646  [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
647  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
648  {
649  int actual_cost = args.u.movement_cost(args.context().get_disp_context().map().get_terrain(args.loc));
650  for(auto cost : ranges) {
651  if(cost.first <= actual_cost && actual_cost <= cost.second) {
652  return true;
653  }
654  }
655  return false;
656  }
657  );
658 
659  create_attribute(literal["vision_cost"],
660  [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
661  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
662  {
663  int actual_cost = args.u.vision_cost(args.context().get_disp_context().map().get_terrain(args.loc));
664  for(auto cost : ranges) {
665  if(cost.first <= actual_cost && actual_cost <= cost.second) {
666  return true;
667  }
668  }
669  return false;
670  }
671  );
672 
673  create_attribute(literal["jamming_cost"],
674  [](const config::attribute_value& c) { return utils::parse_ranges(c.str()); },
675  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
676  {
677  int actual_cost = args.u.jamming_cost(args.context().get_disp_context().map().get_terrain(args.loc));
678  for(auto cost : ranges) {
679  if(cost.first <= actual_cost && actual_cost <= cost.second) {
680  return true;
681  }
682  }
683  return false;
684  }
685  );
686 
687  create_attribute(literal["lua_function"],
688  [](const config::attribute_value& c) { return c.str(); },
689  [](const std::string& lua_function, const unit_filter_args& args)
690  {
691  if (game_lua_kernel * lk = args.context().get_lua_kernel()) {
692  return lk->run_filter(lua_function.c_str(), args.u);
693  }
694  return true;
695  }
696  );
697 
698  create_attribute(literal["formula"],
699  [](const config::attribute_value& c)
700  {
701  //TODO: catch syntax error.
703  },
704  [](const wfl::formula& form, const unit_filter_args& args)
705  {
706  try {
707  const wfl::unit_callable main(args.loc, args.u);
708  wfl::map_formula_callable callable(main.fake_ptr());
709  if (args.u2) {
710  std::shared_ptr<wfl::unit_callable> secondary(new wfl::unit_callable(*args.u2));
711  callable.add("other", wfl::variant(secondary));
712  // It's not destroyed upon scope exit because the variant holds a reference
713  }
714  if(!form.evaluate(callable).as_bool()) {
715  return false;
716  }
717  return true;
718  } catch(const wfl::formula_error& e) {
719  lg::log_to_chat() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
720  ERR_WML << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")";
721  // Formulae with syntax errors match nothing
722  return false;
723  }
724  }
725  );
726 
727  create_attribute(literal["find_in"],
728  [](const config::attribute_value& c) { return c.str(); },
729  [](const std::string& find_in, const unit_filter_args& args)
730  {
731  // Allow filtering by searching a stored variable of units
732  if (const game_data * gd = args.context().get_game_data()) {
733  try
734  {
735  for (const config& c : gd->get_variable_access_read(find_in).as_array())
736  {
737  if(c["id"] == args.u.id()) {
738  return true;
739  }
740  }
741  return false;
742  }
743  catch(const invalid_variablename_exception&)
744  {
745  return false;
746  }
747  }
748  return true;
749  }
750  );
751 
752  if (!literal["x"].blank() || !literal["y"].blank()) {
753  children_.emplace_back(new unit_filter_xy(literal["x"], literal["y"]));
754  }
755 
756  for(auto child : cfg.all_ordered()) {
757  auto cond = conditional_type::get_enum(child.first);
758  if(cond) {
759  cond_children_.emplace_back(std::piecewise_construct_t(), std::tuple(*cond), std::tuple(child.second));
760  }
761  else if (child.first == "filter_wml") {
762  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
763  config fwml = c.get_parsed_config();
764 
765  /* Check if the filter only cares about variables.
766  If so, no need to serialize the whole unit. */
767  config::all_children_itors ci = fwml.all_children_range();
768  if (fwml.all_children_count() == 1 && fwml.attribute_count() == 1 && ci.front().key == "variables") {
769  return args.u.variables().matches(ci.front().cfg);
770  } else {
771  config ucfg;
772  args.u.write(ucfg);
773  return ucfg.matches(fwml);
774  }
775  });
776  }
777  else if (child.first == "filter_vision") {
778  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
779  std::set<int> viewers;
780  side_filter ssf(c, args.fc);
781  std::vector<int> sides = ssf.get_teams();
782  viewers.insert(sides.begin(), sides.end());
783 
784  for (const int viewer : viewers) {
785  bool fogged = args.context().get_disp_context().get_team(viewer).fogged(args.loc);
786  // Check is_enemy() before invisible() to prevent infinite recursion in [abilities][hides][filter_self][filter_vision]
787  bool hiding = args.context().get_disp_context().get_team(viewer).is_enemy(args.u.side()) && args.u.invisible(args.loc);
788  bool unit_hidden = fogged || hiding;
789  if (c["visible"].to_bool(true) != unit_hidden) {
790  return true;
791  }
792  }
793  return false;
794  });
795  }
796  else if (child.first == "filter_adjacent") {
797  children_.emplace_back(new unit_filter_adjacent(child.second));
798  }
799  else if (child.first == "filter_location") {
800  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
801  return terrain_filter(c, args.fc, args.use_flat_tod).match(args.loc);
802  });
803  }
804  else if (child.first == "filter_side") {
805  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
806  return side_filter(c, args.fc).match(args.u.side());
807  });
808  }
809  else if (child.first == "has_attack") {
810  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
811  for(const attack_type& a : args.u.attacks()) {
812  if(a.matches_filter(c.get_parsed_config())) {
813  return true;
814  }
815  }
816  return false;
817  });
818  }
819  else {
820  std::stringstream errmsg;
821  errmsg << "encountered a child [" << child.first << "] of a standard unit filter, it is being ignored";
822  DBG_CF << errmsg.str();
823  }
824 
825  }
826  }
static lg::log_domain log_config("config")
std::function< int(lua_State *)> lua_function
bool empty() const
Tests for an attribute that either was never set or was set to "".
unit_iterator end()
Definition: map.hpp:429
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:98
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:978
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
const team & get_team(int side) const
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1246
virtual const display_context & get_disp_context() const =0
const filter_context * fc_
Definition: filter.hpp:187
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
The unit cannot be healed.
Definition: unit.hpp:869
This class represents a single unit of a specific type.
Definition: unit.hpp:133
int max_matches_
Definition: filter.hpp:190
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
#define DBG_CF
Definition: filter.cpp:44
int main(int, char **argv)
Definition: sdl2.cpp:5
Variant for storing WML attributes.
std::string filename
Definition: formula.hpp:108
#define a
void create_child(const vconfig &c, F func)
Definition: filter.cpp:279
bool operator()(const unit &u, const map_location &loc) const
Definition: filter.hpp:144
vconfig cfg_
Definition: filter.hpp:186
unit_iterator begin()
Definition: map.hpp:419
const unit_map & get_units() const
Definition: display.hpp:131
unit_type_data unit_types
Definition: types.cpp:1465
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp >> &ranges)
Definition: math.hpp:87
bool matches(const unit &u, const map_location &loc) const
Determine if *this matches filter at a specified location.
Definition: filter.hpp:127
bool filter_impl(const unit_filter_args &u) const
Definition: filter.cpp:268
const filter_context & context() const
Definition: filter.hpp:66
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
#define ERR_WML
Definition: filter.cpp:47
Definitions for the interface to Wesnoth Markup Language (WML).
virtual const gamemap & map() const =0
unit_filter(vconfig cfg)
Definition: filter.cpp:51
A single unit type that the player may recruit.
Definition: types.hpp:45
game_data * gamedata
Definition: resources.cpp:23
utils::variant< upkeep_full, upkeep_loyal, int > upkeep_t
Definition: unit.hpp:1140
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
static T & get_lua_kernel(lua_State *L)
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
std::vector< std::pair< int, int > > default_counts
unit_filter_impl::unit_filter_compound impl_
Definition: filter.hpp:189
filter_context * filter_con
Definition: resources.cpp:24
bool valid() const
Definition: location.hpp:89
std::string type
Definition: formula.hpp:106
bool blank() const
Tests for an attribute that was never set.
bool use_flat_tod_
Definition: filter.hpp:188
std::vector< std::pair< conditional_type::type, unit_filter_compound > > cond_children_
Definition: filter.hpp:101
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
Definition: race.cpp:152
static const std::vector< DIRECTION > & default_dirs()
Default list of directions.
Definition: location.cpp:53
map_display and display: classes which take care of displaying the map and game-data on the screen...
virtual const unit_map & units() const =0
const filter_context * fc
Definition: filter.hpp:63
Encapsulates the map of the game.
Definition: location.hpp:38
unit_iterator find(std::size_t id)
Definition: map.cpp:301
unit_const_ptr first_match_on_map() const
Definition: filter.cpp:85
std::vector< std::shared_ptr< unit_filter_base > > children_
Definition: filter.hpp:100
std::size_t i
Definition: function.cpp:968
formula_callable_ptr fake_ptr()
Definition: callable.hpp:42
Visitor helper class to fetch the appropriate upkeep value.
Definition: unit.hpp:1143
static constexpr std::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
static map_location::DIRECTION s
pump_impl & impl_
Definition: pump.cpp:134
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
bool to_bool(bool def=false) const
bool matches_range(const std::string &xloc, const std::string &yloc) const
Definition: location.cpp:318
void create_attribute(const config::attribute_value c, C conv, F func)
Definition: filter.cpp:285
std::vector< const unit * > all_matches_on_map(const map_location *loc=nullptr, const unit *other_unit=nullptr) const
Definition: filter.cpp:68
#define f
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:1360
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
const config & get_config() const
Definition: variable.hpp:75
Standard logging facilities (interface).
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
Container associating units to locations.
Definition: map.hpp:98
#define e
int side() const
The side this unit belongs to.
Definition: unit.hpp:346
#define WRN_CF
Definition: filter.cpp:43
Visitor helper class to parse the upkeep value from a config.
Definition: unit.hpp:1193
bool as_bool() const
Returns a boolean state of the variant value.
Definition: variant.cpp:316
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
mock_char c
virtual bool matches(const unit_filter_args &u) const override
Definition: filter.cpp:233
bool has_variation(const std::string &variation_id) const
Definition: types.cpp:757
static lg::log_domain log_wml("wml")
bool empty() const
Definition: config.cpp:941
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:458
std::string str(const std::string &fallback="") const
boost::iterator_range< all_children_iterator > all_ordered() const
Definition: variable.hpp:190
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46