The Battle for Wesnoth  1.19.26+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
34 #include "formula/formula.hpp"
36 #include "formula/string_utils.hpp"
37 #include "resources.hpp"
38 #include "deprecation.hpp"
39 #include "utils/general.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  std::size_t radius = cfg_["radius"].to_int(1);
135 
136  config::attribute_value i_adjacent = cfg_["adjacent"];
137  std::vector<map_location::direction> dirs;
138  for(const unit& u : units) {
139  const map_location& from_loc = u.get_location();
140  std::size_t distance = distance_between(from_loc, args.loc);
141  if(u.underlying_id() == args.u.underlying_id() || distance > radius || !child_.matches(unit_filter_args{u, from_loc, &args.u, args.fc, args.use_flat_tod} )) {
142  continue;
143  }
144  int dir = 0;
145  for(unsigned j = 0; j < adjacent.size(); ++j) {
146  bool adj_or_dist = distance != 1 ? distance_between(adjacent[j], from_loc) == (distance - 1) : adjacent[j] == from_loc;
147  if(adj_or_dist) {
148  dir = j;
149  break;
150  }
151  }
152  assert(dir >= 0 && dir <= 5);
153  map_location::direction direction{ dir };
154  if(!i_adjacent.empty()) { //key adjacent defined
155  if(!utils::contains(map_location::parse_directions(i_adjacent), direction)) {
156  continue;
157  }
158  }
159  auto is_enemy = cfg_["is_enemy"];
160  if (!is_enemy.empty() && is_enemy.to_bool() != args.context().get_disp_context().get_team(args.u.side()).is_enemy(u.side())) {
161  continue;
162  }
163  ++match_count;
164  }
165 
166  static std::vector<std::pair<int,int>> default_counts = {{{1, std::numeric_limits<int>::max()}}};
167  config::attribute_value i_count = cfg_["count"];
168  return in_ranges(match_count, !i_count.empty() ? utils::parse_ranges_unsigned(i_count) : default_counts);
169  }
170 
171  const unit_filter_compound child_;
172  const vconfig cfg_;
173 };
174 
175 
176 template<typename F>
177 struct unit_filter_child_literal : public unit_filter_base
178 {
179  unit_filter_child_literal(const vconfig& v, const F& f) : v_(v) , f_(f) {}
180  virtual bool matches(const unit_filter_args& args) const override
181  {
182  return f_(v_, args);
183  }
184  vconfig v_;
185  F f_;
186 };
187 
188 template<typename T, typename F>
189 struct unit_filter_attribute_parsed : public unit_filter_base
190 {
191  unit_filter_attribute_parsed(T&& v, F&& f) : v_(std::move(v)), f_(std::move(f)) {}
192  virtual bool matches(const unit_filter_args& args) const override
193  {
194  return f_(v_, args);
195  }
196  T v_;
197  F f_;
198 };
199 
200 template<typename C, typename F>
201 struct unit_filter_attribute_literal : public unit_filter_base
202 {
203  unit_filter_attribute_literal(std::string&& v, C&& c, F&& f) : v_(std::move(v)), c_(std::move(c)), f_(std::move(f)) {}
204  virtual bool matches(const unit_filter_args& args) const override
205  {
208  return f_(c_(v), args);
209  }
210  std::string v_;
211  C c_;
212  F f_;
213 };
214 
215 class contains_dollar_visitor
216 #ifdef USING_BOOST_VARIANT
217  : public boost::static_visitor<bool>
218 #endif
219 {
220 public:
221  contains_dollar_visitor() {}
222 
223 
224  template<typename T>
225  bool operator()(const T&) const { return false; }
226 
227  bool operator()(const t_string&) const { return true; }
228 
229  bool operator()(const std::string& s) const
230  {
231  return s.find('$') != std::string::npos;
232  }
233 };
234 
235 }
236 
237 
238 unit_filter_compound::unit_filter_compound(const vconfig& cfg)
239  : children_()
240  , cond_children_()
241 {
242  fill(cfg);
243 }
244 
246 {
247  bool res;
248 
249  if(args.loc.valid()) {
250  scoped_xy_unit auto_store("this_unit", args.u.get_location(), args.context().get_disp_context().units());
251  if (args.u2) {
252  const map_location& loc2 = args.u2->get_location();
253  scoped_xy_unit u2_auto_store("other_unit", loc2, args.context().get_disp_context().units());
254  res = filter_impl(args);
255  } else {
256  res = filter_impl(args);
257  }
258  } else {
259  // If loc is invalid, then this is a recall list unit (already been scoped)
260  res = filter_impl(args);
261  }
262 
263  // Handle [and], [or], and [not] with in-order precedence
264  for(const auto & filter : cond_children_) {
265  switch (filter.first) {
266  case conditional_type::type::filter_and:
267  res = res && filter.second.matches(args);
268  break;
269  case conditional_type::type::filter_or:
270  res = res || filter.second.matches(args);
271  break;
272  case conditional_type::type::filter_not:
273  res = res && !filter.second.matches(args);
274  break;
275  }
276  }
277  return res;
278 }
279 
281 {
282  for(const auto & filter : children_) {
283  if (!filter->matches(args)) {
284  return false;
285  }
286  }
287  return true;
288 }
289 
290 template<typename F>
292 {
293  children_.emplace_back(new unit_filter_child_literal<F>(c, func));
294 }
295 
296 template<typename C, typename F>
298 {
299  if(v.blank()) {
300  }
301  else if(v.apply_visitor(contains_dollar_visitor())) {
302  children_.emplace_back(new unit_filter_attribute_literal<C, F>(std::move(v.str()), std::move(conv), std::move(func)));
303  }
304  else {
305  children_.emplace_back(new unit_filter_attribute_parsed<decltype(conv(v)), F>(std::move(conv(v)), std::move(func)));
306  }
307 }
308 
310  {
311  const config& literal = cfg.get_config();
312 
313  //optimisation
314  if(literal.empty()) { return; }
315 
316  create_attribute(literal["name"],
317  [](const config::attribute_value& c) { return c.t_str(); },
318  [](const t_string& str, const unit_filter_args& args) { return str == args.u.name(); }
319  );
320 
321  create_attribute(literal["id"],
322  [](const config::attribute_value& c) { return utils::split(c.str()); },
323  [](const std::vector<std::string>& id_list, const unit_filter_args& args)
324  {
325  return utils::contains(id_list, args.u.id());
326  }
327  );
328 
329  create_attribute(literal["type"],
330  [](const config::attribute_value& c) { return utils::split(c.str()); },
331  [](const std::vector<std::string>& types, const unit_filter_args& args)
332  {
333  return utils::contains(types, args.u.type_id());
334  }
335  );
336 
337  create_attribute(literal["type_adv_tree"],
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  std::set<std::string> types_expanded;
342  for(const std::string& type : types) {
343  if(types_expanded.count(type)) {
344  continue;
345  }
346  if(const unit_type* ut = unit_types.find(type)) {
347  const auto& tree = ut->advancement_tree();
348  types_expanded.insert(tree.begin(), tree.end());
349  types_expanded.insert(type);
350  }
351  }
352  return utils::contains(types_expanded, args.u.type_id());
353  }
354  );
355 
356  create_attribute(literal["variation"],
357  [](const config::attribute_value& c) { return utils::split(c.str()); },
358  [](const std::vector<std::string>& types, const unit_filter_args& args)
359  {
360  return utils::contains(types, args.u.variation());
361  }
362  );
363 
364  create_attribute(literal["has_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  // If this unit is a variation itself then search in the base unit's variations.
369  const unit_type* const type = args.u.variation().empty() ? &args.u.type() : unit_types.find(args.u.type().parent_id());
370  assert(type);
371 
372  for(const std::string& variation_id : types) {
373  if (type->has_variation(variation_id)) {
374  return true;
375  }
376  }
377  return false;
378  }
379  );
380 
381  create_attribute(literal["ability"],
382  [](const config::attribute_value& c) { return utils::split(c.str()); },
383  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
384  {
385  for(const std::string& ability_id : abilities) {
386  if (args.u.has_ability_by_id(ability_id)) {
387  return true;
388  }
389  }
390  return false;
391  }
392  );
393 
394  create_attribute(literal["ability_type"],
395  [](const config::attribute_value& c) { return utils::split(c.str()); },
396  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
397  {
398  for(const std::string& ability : abilities) {
399  if (args.u.has_ability_type(ability)) {
400  return true;
401  }
402  }
403  return false;
404  }
405  );
406 
407  create_attribute(literal["ability_id_active"],
408  [](const config::attribute_value& c) { return utils::split_set(c.str()); },
409  [](const std::set<std::string>& abilities, const unit_filter_args& args)
410  {
411  return utils::find_if(abilities, [&](const std::string& abilitiy_id) { return specials_context_t::has_active_ability_id(args.u, args.loc, abilitiy_id); });
412  }
413  );
414 
415  create_attribute(literal["ability_type_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  for(const std::string& ability : abilities) {
420  if (!args.u.get_abilities(ability, args.loc).empty()) {
421  return true;
422  }
423  }
424  return false;
425  }
426  );
427 
428  create_attribute(literal["trait"],
429  [](const config::attribute_value& c)
430  {
431  auto res = utils::split(c.str());
432  std::sort(res.begin(), res.end());
433  return res;
434 
435  },
436  [](const std::vector<std::string>& check_traits, const unit_filter_args& args)
437  {
438 
439  std::vector<std::string> have_traits = args.u.get_traits_list();
440  std::vector<std::string> isect;
441  std::sort(have_traits.begin(), have_traits.end());
442  std::set_intersection(check_traits.begin(), check_traits.end(), have_traits.begin(), have_traits.end(), std::back_inserter(isect));
443  return !isect.empty();
444  }
445  );
446 
447  create_attribute(literal["race"],
448  [](const config::attribute_value& c) { return utils::split(c.str()); },
449  [](const std::vector<std::string>& races, const unit_filter_args& args)
450  {
451  return utils::contains(races, args.u.race()->id());
452  }
453  );
454 
455  create_attribute(literal["gender"],
456  [](const config::attribute_value& c) { return string_gender(c.str()); },
457  [](unit_race::GENDER gender, const unit_filter_args& args)
458  {
459  return gender == args.u.gender();
460  }
461  );
462 
463  create_attribute(literal["upkeep"],
465  {
466  try {
467  return c.apply_visitor(unit::upkeep_parser_visitor());
468  } catch(std::invalid_argument&) {
469  return unit::upkeep_full();
470  }
471  },
472  [](unit::upkeep_t upkeep, const unit_filter_args& args)
473  {
474  return args.u.upkeep() == utils::visit(unit::upkeep_value_visitor{args.u}, upkeep);
475  }
476  );
477 
478  create_attribute(literal["side"],
479  [](const config::attribute_value& c)
480  {
481  std::vector<int> res;
482  for(const std::string& s : utils::split(c.str())) {
483  try {
484  res.push_back(std::stoi(s));
485  } catch(std::invalid_argument&) {
486  WRN_CF << "ignored invalid side='" << s << "' in filter";
487  }
488  }
489  return res;
490  },
491  [](const std::vector<int>& sides, const unit_filter_args& args)
492  {
493  return utils::contains(sides, args.u.side());
494  }
495  );
496 
497  create_attribute(literal["status"],
498  [](const config::attribute_value& c) { return utils::split(c.str()); },
499  [](const std::vector<std::string>& statuses, const unit_filter_args& args)
500  {
501  for(const std::string& status : statuses) {
502  if (args.u.get_state(status)) {
503  return true;
504  }
505  }
506  return false;
507  }
508  );
509 
510  create_attribute(literal["has_weapon"],
511  [](const config::attribute_value& c) { return c.str(); },
512  [](const std::string& weapon, const unit_filter_args& args)
513  {
514 
515  for(const attack_type& a : args.u.attacks()) {
516  if(a.id() == weapon) {
517  return true;
518  }
519  }
520  return false;
521  }
522  );
523 
524  create_attribute(literal["role"],
525  [](const config::attribute_value& c) { return c.str(); },
526  [](const std::string& role, const unit_filter_args& args)
527  {
528  return args.u.get_role() == role;
529  }
530  );
531 
532  create_attribute(literal["alignment"],
533  [](const config::attribute_value& c) { return c.str(); },
534  [](const std::string& alignment, const unit_filter_args& args)
535  {
536  return unit_alignments::get_string(args.u.alignment()) == alignment;
537  }
538  );
539 
540  create_attribute(literal["ai_special"],
541  [](const config::attribute_value& c) { return c.str(); },
542  [](const std::string& ai_special, const unit_filter_args& args)
543  {
544  return (ai_special == "guardian") == args.u.get_state(unit::STATE_GUARDIAN);
545  }
546  );
547 
548  create_attribute(literal["usage"],
549  [](const config::attribute_value& c) { return utils::split(c.str()); },
550  [](const std::vector<std::string>& usages, const unit_filter_args& args)
551  {
552  for(const std::string& usage : usages) {
553  if(args.u.usage() == usage) {
554  return true;
555  }
556  }
557  return false;
558  }
559  );
560 
561  create_attribute(literal["canrecruit"],
562  [](const config::attribute_value& c) { return c.to_bool(); },
563  [](bool canrecruit, const unit_filter_args& args)
564  {
565  return args.u.can_recruit() == canrecruit;
566  }
567  );
568 
569  create_attribute(literal["recall_cost"],
570  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
571  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
572  {
573  return in_ranges(args.u.recall_cost(), ranges);
574  }
575  );
576 
577  create_attribute(literal["level"],
578  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
579  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
580  {
581  return in_ranges(args.u.level(), ranges);
582  }
583  );
584 
585  create_attribute(literal["defense"],
586  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
587  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
588  {
589  int actual_defense = args.u.defense_modifier(args.context().get_disp_context().map().get_terrain(args.loc));
590  return in_ranges(actual_defense, ranges);
591  }
592  );
593 
594  create_attribute(literal["movement_cost"],
595  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
596  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
597  {
598  int actual_cost = args.u.movement_cost(args.context().get_disp_context().map().get_terrain(args.loc));
599  return in_ranges(actual_cost, ranges);
600  }
601  );
602 
603  create_attribute(literal["vision_cost"],
604  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
605  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
606  {
607  int actual_cost = args.u.vision_cost(args.context().get_disp_context().map().get_terrain(args.loc));
608  return in_ranges(actual_cost, ranges);
609  }
610  );
611 
612  create_attribute(literal["jamming_cost"],
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  int actual_cost = args.u.jamming_cost(args.context().get_disp_context().map().get_terrain(args.loc));
617  return in_ranges(actual_cost, ranges);
618  }
619  );
620 
621  create_attribute(literal["lua_function"],
622  [](const config::attribute_value& c) { return c.str(); },
623  [](const std::string& lua_function, const unit_filter_args& args)
624  {
625  if (game_lua_kernel * lk = args.context().get_lua_kernel()) {
626  return lk->run_filter(lua_function.c_str(), args.u);
627  }
628  return true;
629  }
630  );
631 
632  create_attribute(literal["formula"],
633  [](const config::attribute_value& c)
634  {
635  try {
637  } catch(const wfl::formula_error& e) {
638  lg::log_to_chat() << "Formula error while evaluating formula in unit filter: " << e.type << " at "
639  << e.filename << ':' << e.line << ")\n";
640  ERR_WML << "Formula error while evaluating formula in unit filter: " << e.type << " at "
641  << e.filename << ':' << e.line << ")";
642  return wfl::formula("");
643  }
644  },
645  [](const wfl::formula& form, const unit_filter_args& args)
646  {
647  try {
648  const wfl::unit_callable main(args.loc, args.u);
649  wfl::map_formula_callable callable(main.fake_ptr());
650  if (args.u2) {
651  auto secondary = std::make_shared<wfl::unit_callable>(*args.u2);
652  callable.add("other", wfl::variant(secondary));
653  // It's not destroyed upon scope exit because the variant holds a reference
654  }
655  if(!form.evaluate(callable).as_bool()) {
656  return false;
657  }
658  return true;
659  } catch(const wfl::formula_error& e) {
660  lg::log_to_chat() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
661  ERR_WML << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")";
662  // Formulae with syntax errors match nothing
663  return false;
664  }
665  }
666  );
667 
668  create_attribute(literal["find_in"],
669  [](const config::attribute_value& c) { return c.str(); },
670  [](const std::string& find_in, const unit_filter_args& args)
671  {
672  // Allow filtering by searching a stored variable of units
673  if (const game_data * gd = args.context().get_game_data()) {
674  try
675  {
676  for (const config& c : gd->get_variable_access_read(find_in).as_array())
677  {
678  if(c["id"] == args.u.id()) {
679  return true;
680  }
681  }
682  return false;
683  }
684  catch(const invalid_variablename_exception&)
685  {
686  return false;
687  }
688  }
689  return true;
690  }
691  );
692 
693  if (!literal["x"].blank() || !literal["y"].blank()) {
694  children_.emplace_back(new unit_filter_xy(literal["x"], literal["y"]));
695  }
696 
697  for(auto child : cfg.all_ordered()) {
698  auto cond = conditional_type::get_enum(child.first);
699  if(cond) {
700  cond_children_.emplace_back(std::piecewise_construct_t(), std::tuple(*cond), std::tuple(child.second));
701  }
702  else if (child.first == "filter_wml") {
703  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
704  config fwml = c.get_parsed_config();
705 
706  /* Check if the filter only cares about variables.
707  If so, no need to serialize the whole unit. */
708  config::all_children_itors ci = fwml.all_children_range();
709  if (fwml.all_children_count() == 1 && fwml.attribute_count() == 0 && ci.front().key == "variables") {
710  return args.u.variables().matches(ci.front().cfg);
711  } else {
712  config ucfg;
713  args.u.write(ucfg);
714  return ucfg.matches(fwml);
715  }
716  });
717  }
718  else if (child.first == "filter_vision") {
719  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
720  std::set<int> viewers;
721  side_filter ssf(c, args.fc);
722  std::vector<int> sides = ssf.get_teams();
723  viewers.insert(sides.begin(), sides.end());
724 
725  for (const int viewer : viewers) {
726  bool fogged = args.context().get_disp_context().get_team(viewer).fogged(args.loc);
727  // Check is_enemy() before invisible() to prevent infinite recursion in [abilities][hides][filter_self][filter_vision]
728  bool hiding = args.context().get_disp_context().get_team(viewer).is_enemy(args.u.side()) && args.u.invisible(args.loc);
729  bool unit_hidden = fogged || hiding;
730  if (c["visible"].to_bool(true) != unit_hidden) {
731  return true;
732  }
733  }
734  return false;
735  });
736  }
737  else if (child.first == "filter_adjacent") {
738  children_.emplace_back(new unit_filter_adjacent(child.second));
739  }
740  else if (child.first == "filter_location") {
741  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
742  return terrain_filter(c, args.fc, args.use_flat_tod).match(args.loc);
743  });
744  }
745  else if (child.first == "filter_side") {
746  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
747  return side_filter(c, args.fc).match(args.u.side());
748  });
749  }
750  else if ((child.first == "filter_ability") || (child.first == "experimental_filter_ability")) {
751  if(child.first == "experimental_filter_ability"){
752  deprecated_message("experimental_filter_ability", DEP_LEVEL::FOR_REMOVAL, {1, 21, 0}, "Use filter_ability instead.");
753  }
754  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
755  if(!(c.get_parsed_config())["active"].to_bool()){
756  for(const ability_ptr& p_ab : args.u.abilities()) {
757  if(p_ab->matches_filter(c.get_parsed_config())) {
758  return true;
759  }
760  }
761  } else {
762  return specials_context_t::has_active_ability_matching_filter(args.u, args.loc, c.get_parsed_config());
763  }
764  return false;
765  });
766  }
767  else if (child.first == "has_attack") {
768  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
769  for(const attack_type& a : args.u.attacks()) {
770  if(a.matches_filter(c.get_parsed_config())) {
771  return true;
772  }
773  }
774  return false;
775  });
776  }
777  else {
778  std::stringstream errmsg;
779  errmsg << "encountered a child [" << child.first << "] of a standard unit filter, it is being ignored";
780  DBG_CF << errmsg.str();
781  }
782 
783  }
784  }
static bool is_enemy(std::size_t side, std::size_t other_side)
Definition: abilities.cpp:1063
map_location loc
Definition: move.cpp:172
bool empty() const
Definition: abilities.hpp:221
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:157
bool empty() const
Definition: config.cpp:823
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:265
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:347
static bool has_active_ability_id(const unit &un, map_location loc, const std::string &id)
Definition: abilities.cpp:1878
bool is_enemy(int n) const
Definition: team.hpp:270
Visitor helper class to parse the upkeep value from a config.
Definition: unit.hpp:1131
Visitor helper class to fetch the appropriate upkeep value.
Definition: unit.hpp:1081
unit_filter(const vconfig &cfg)
Definition: filter.cpp:51
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:68
unit_const_ptr first_match_on_map() const
Definition: filter.cpp:85
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:1253
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:149
This class represents a single unit of a specific type.
Definition: unit.hpp:39
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
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:357
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
const config * cfg
Interfaces for manipulating version numbers of engine, add-ons, etc.
active_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:708
bool has_ability_type(const std::string &ability) const
Check if the unit has an ability of a specific type.
Definition: abilities.cpp:851
bool has_ability_by_id(const std::string &ability) const
Check if the unit has an ability of a specific ID.
Definition: unit.cpp:1518
unit_alignments::type alignment() const
The alignment of this unit.
Definition: unit.hpp:397
int level() const
The current level of this unit.
Definition: unit.hpp:481
std::string usage() const
Gets this unit's usage.
Definition: unit.hpp:608
const std::string & get_role() const
Gets this unit's role.
Definition: unit.hpp:591
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:562
const std::string & variation() const
The ID of the variation of this unit's type.
Definition: unit.hpp:494
bool get_state(const std::string &state) const
Check if the unit is affected by a status effect.
Definition: unit.cpp:1436
const std::string & type_id() const
The id of this unit's type.
Definition: unit.cpp:1954
const unit_race * race() const
Gets this unit's race.
Definition: unit.hpp:415
const unit_type & type() const
This unit's type, accounting for gender and variation.
Definition: unit.hpp:261
bool can_recruit() const
Whether this unit can recruit other units - ie, are they a leader unit.
Definition: unit.hpp:534
const std::string & id() const
Gets this unit's id.
Definition: unit.hpp:286
int side() const
The side this unit belongs to.
Definition: unit.hpp:249
std::size_t underlying_id() const
This unit's unique internal ID.
Definition: unit.hpp:298
unit_race::GENDER gender() const
The gender of this unit.
Definition: unit.hpp:387
const t_string & name() const
Gets this unit's translatable display name.
Definition: unit.hpp:309
@ STATE_GUARDIAN
The unit cannot be healed.
Definition: unit.hpp:781
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition: unit.hpp:848
int defense_modifier(const t_translation::terrain_code &terrain, const map_location &loc) const
The unit's defense on a given terrain.
Definition: unit.cpp:1767
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1327
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
Definition: unit.hpp:1410
int vision_cost(const t_translation::terrain_code &terrain) const
Get the unit's vision cost on a particular terrain.
Definition: unit.hpp:1420
int jamming_cost(const t_translation::terrain_code &terrain) const
Get the unit's jamming cost on a particular terrain.
Definition: unit.hpp:1430
int upkeep() const
Gets the amount of gold this unit costs a side per turn.
Definition: unit.cpp:1741
utils::variant< upkeep_full, upkeep_loyal, int > upkeep_t
Definition: unit.hpp:1074
std::vector< std::string > get_traits_list() const
Gets a list of the traits this unit currently has, including hidden traits.
Definition: unit.hpp:1027
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:584
void get_adjacent_tiles(const map_location &a, utils::span< map_location, 6 > res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:513
Standard logging facilities (interface).
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp >> &ranges)
Definition: math.hpp:85
std::vector< std::pair< int, int > > default_counts
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp: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:42
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:87
auto * find_if(Container &container, const Predicate &predicate)
Convenience wrapper for using find_if on a container without needing to comare to end()
Definition: general.hpp:151
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...
auto find_in(Container &map, const Value &value) -> utils::optional_reference< typename Container::mapped_type >
Definition: sound.cpp:43
std::vector< std::string > split(const config_attribute_value &val)
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:149
int main(int, char **)
Definition: sdl3.cpp:25
Encapsulates the map of the game.
Definition: location.hpp:46
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:111
direction
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:48
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:280
void fill(const vconfig &cfg)
Definition: filter.cpp:309
void create_child(const vconfig &c, F func)
Definition: filter.cpp:291
void create_attribute(const config::attribute_value &c, C conv, F func)
Definition: filter.cpp:297
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:245
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:1494
#define DBG_CF
Definition: filter.cpp:44
#define ERR_WML
Definition: filter.cpp:47
static lg::log_domain log_wml("wml")
static lg::log_domain log_config("config")
#define e
#define f