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