The Battle for Wesnoth  1.19.20+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 
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(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} )) {
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 utils::contains(id_list, args.u.id());
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 utils::contains(types, args.u.type_id());
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 utils::contains(types_expanded, args.u.type_id());
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 utils::contains(types, args.u.variation());
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  return utils::find_if(abilities, [&](const std::string& abilitiy_id) { return specials_context_t::has_active_ability_id(args.u, args.loc, abilitiy_id); });
416  }
417  );
418 
419  create_attribute(literal["ability_type_active"],
420  [](const config::attribute_value& c) { return utils::split(c.str()); },
421  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
422  {
423  for(const std::string& ability : abilities) {
424  if (!args.u.get_abilities(ability, args.loc).empty()) {
425  return true;
426  }
427  }
428  return false;
429  }
430  );
431 
432  create_attribute(literal["trait"],
433  [](const config::attribute_value& c)
434  {
435  auto res = utils::split(c.str());
436  std::sort(res.begin(), res.end());
437  return res;
438 
439  },
440  [](const std::vector<std::string>& check_traits, const unit_filter_args& args)
441  {
442 
443  std::vector<std::string> have_traits = args.u.get_traits_list();
444  std::vector<std::string> isect;
445  std::sort(have_traits.begin(), have_traits.end());
446  std::set_intersection(check_traits.begin(), check_traits.end(), have_traits.begin(), have_traits.end(), std::back_inserter(isect));
447  return !isect.empty();
448  }
449  );
450 
451  create_attribute(literal["race"],
452  [](const config::attribute_value& c) { return utils::split(c.str()); },
453  [](const std::vector<std::string>& races, const unit_filter_args& args)
454  {
455  return utils::contains(races, args.u.race()->id());
456  }
457  );
458 
459  create_attribute(literal["gender"],
460  [](const config::attribute_value& c) { return string_gender(c.str()); },
461  [](unit_race::GENDER gender, const unit_filter_args& args)
462  {
463  return gender == args.u.gender();
464  }
465  );
466 
467  create_attribute(literal["upkeep"],
469  {
470  try {
471  return c.apply_visitor(unit::upkeep_parser_visitor());
472  } catch(std::invalid_argument&) {
473  return unit::upkeep_full();
474  }
475  },
476  [](unit::upkeep_t upkeep, const unit_filter_args& args)
477  {
478  return args.u.upkeep() == utils::visit(unit::upkeep_value_visitor{args.u}, upkeep);
479  }
480  );
481 
482  create_attribute(literal["side"],
483  [](const config::attribute_value& c)
484  {
485  std::vector<int> res;
486  for(const std::string& s : utils::split(c.str())) {
487  try {
488  res.push_back(std::stoi(s));
489  } catch(std::invalid_argument&) {
490  WRN_CF << "ignored invalid side='" << s << "' in filter";
491  }
492  }
493  return res;
494  },
495  [](const std::vector<int>& sides, const unit_filter_args& args)
496  {
497  return utils::contains(sides, args.u.side());
498  }
499  );
500 
501  create_attribute(literal["status"],
502  [](const config::attribute_value& c) { return utils::split(c.str()); },
503  [](const std::vector<std::string>& statuses, const unit_filter_args& args)
504  {
505  for(const std::string& status : statuses) {
506  if (args.u.get_state(status)) {
507  return true;
508  }
509  }
510  return false;
511  }
512  );
513 
514  create_attribute(literal["has_weapon"],
515  [](const config::attribute_value& c) { return c.str(); },
516  [](const std::string& weapon, const unit_filter_args& args)
517  {
518 
519  for(const attack_type& a : args.u.attacks()) {
520  if(a.id() == weapon) {
521  return true;
522  }
523  }
524  return false;
525  }
526  );
527 
528  create_attribute(literal["role"],
529  [](const config::attribute_value& c) { return c.str(); },
530  [](const std::string& role, const unit_filter_args& args)
531  {
532  return args.u.get_role() == role;
533  }
534  );
535 
536  create_attribute(literal["alignment"],
537  [](const config::attribute_value& c) { return c.str(); },
538  [](const std::string& alignment, const unit_filter_args& args)
539  {
540  return unit_alignments::get_string(args.u.alignment()) == alignment;
541  }
542  );
543 
544  create_attribute(literal["ai_special"],
545  [](const config::attribute_value& c) { return c.str(); },
546  [](const std::string& ai_special, const unit_filter_args& args)
547  {
548  return (ai_special == "guardian") == args.u.get_state(unit::STATE_GUARDIAN);
549  }
550  );
551 
552  create_attribute(literal["usage"],
553  [](const config::attribute_value& c) { return utils::split(c.str()); },
554  [](const std::vector<std::string>& usages, const unit_filter_args& args)
555  {
556  for(const std::string& usage : usages) {
557  if(args.u.usage() == usage) {
558  return true;
559  }
560  }
561  return false;
562  }
563  );
564 
565  create_attribute(literal["canrecruit"],
566  [](const config::attribute_value& c) { return c.to_bool(); },
567  [](bool canrecruit, const unit_filter_args& args)
568  {
569  return args.u.can_recruit() == canrecruit;
570  }
571  );
572 
573  create_attribute(literal["recall_cost"],
574  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
575  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
576  {
577  return in_ranges(args.u.recall_cost(), ranges);
578  }
579  );
580 
581  create_attribute(literal["level"],
582  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
583  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
584  {
585  return in_ranges(args.u.level(), ranges);
586  }
587  );
588 
589  create_attribute(literal["defense"],
590  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
591  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
592  {
593  int actual_defense = args.u.defense_modifier(args.context().get_disp_context().map().get_terrain(args.loc));
594  return in_ranges(actual_defense, ranges);
595  }
596  );
597 
598  create_attribute(literal["movement_cost"],
599  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
600  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
601  {
602  int actual_cost = args.u.movement_cost(args.context().get_disp_context().map().get_terrain(args.loc));
603  return in_ranges(actual_cost, ranges);
604  }
605  );
606 
607  create_attribute(literal["vision_cost"],
608  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
609  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
610  {
611  int actual_cost = args.u.vision_cost(args.context().get_disp_context().map().get_terrain(args.loc));
612  return in_ranges(actual_cost, ranges);
613  }
614  );
615 
616  create_attribute(literal["jamming_cost"],
617  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
618  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
619  {
620  int actual_cost = args.u.jamming_cost(args.context().get_disp_context().map().get_terrain(args.loc));
621  return in_ranges(actual_cost, ranges);
622  }
623  );
624 
625  create_attribute(literal["lua_function"],
626  [](const config::attribute_value& c) { return c.str(); },
627  [](const std::string& lua_function, const unit_filter_args& args)
628  {
629  if (game_lua_kernel * lk = args.context().get_lua_kernel()) {
630  return lk->run_filter(lua_function.c_str(), args.u);
631  }
632  return true;
633  }
634  );
635 
636  create_attribute(literal["formula"],
637  [](const config::attribute_value& c)
638  {
639  try {
641  } catch(const wfl::formula_error& e) {
642  lg::log_to_chat() << "Formula error while evaluating formula in unit filter: " << e.type << " at "
643  << e.filename << ':' << e.line << ")\n";
644  ERR_WML << "Formula error while evaluating formula in unit filter: " << e.type << " at "
645  << e.filename << ':' << e.line << ")";
646  return wfl::formula("");
647  }
648  },
649  [](const wfl::formula& form, const unit_filter_args& args)
650  {
651  try {
652  const wfl::unit_callable main(args.loc, args.u);
653  wfl::map_formula_callable callable(main.fake_ptr());
654  if (args.u2) {
655  auto secondary = std::make_shared<wfl::unit_callable>(*args.u2);
656  callable.add("other", wfl::variant(secondary));
657  // It's not destroyed upon scope exit because the variant holds a reference
658  }
659  if(!form.evaluate(callable).as_bool()) {
660  return false;
661  }
662  return true;
663  } catch(const wfl::formula_error& e) {
664  lg::log_to_chat() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
665  ERR_WML << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")";
666  // Formulae with syntax errors match nothing
667  return false;
668  }
669  }
670  );
671 
672  create_attribute(literal["find_in"],
673  [](const config::attribute_value& c) { return c.str(); },
674  [](const std::string& find_in, const unit_filter_args& args)
675  {
676  // Allow filtering by searching a stored variable of units
677  if (const game_data * gd = args.context().get_game_data()) {
678  try
679  {
680  for (const config& c : gd->get_variable_access_read(find_in).as_array())
681  {
682  if(c["id"] == args.u.id()) {
683  return true;
684  }
685  }
686  return false;
687  }
688  catch(const invalid_variablename_exception&)
689  {
690  return false;
691  }
692  }
693  return true;
694  }
695  );
696 
697  if (!literal["x"].blank() || !literal["y"].blank()) {
698  children_.emplace_back(new unit_filter_xy(literal["x"], literal["y"]));
699  }
700 
701  for(auto child : cfg.all_ordered()) {
702  auto cond = conditional_type::get_enum(child.first);
703  if(cond) {
704  cond_children_.emplace_back(std::piecewise_construct_t(), std::tuple(*cond), std::tuple(child.second));
705  }
706  else if (child.first == "filter_wml") {
707  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
708  config fwml = c.get_parsed_config();
709 
710  /* Check if the filter only cares about variables.
711  If so, no need to serialize the whole unit. */
712  config::all_children_itors ci = fwml.all_children_range();
713  if (fwml.all_children_count() == 1 && fwml.attribute_count() == 1 && ci.front().key == "variables") {
714  return args.u.variables().matches(ci.front().cfg);
715  } else {
716  config ucfg;
717  args.u.write(ucfg);
718  return ucfg.matches(fwml);
719  }
720  });
721  }
722  else if (child.first == "filter_vision") {
723  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
724  std::set<int> viewers;
725  side_filter ssf(c, args.fc);
726  std::vector<int> sides = ssf.get_teams();
727  viewers.insert(sides.begin(), sides.end());
728 
729  for (const int viewer : viewers) {
730  bool fogged = args.context().get_disp_context().get_team(viewer).fogged(args.loc);
731  // Check is_enemy() before invisible() to prevent infinite recursion in [abilities][hides][filter_self][filter_vision]
732  bool hiding = args.context().get_disp_context().get_team(viewer).is_enemy(args.u.side()) && args.u.invisible(args.loc);
733  bool unit_hidden = fogged || hiding;
734  if (c["visible"].to_bool(true) != unit_hidden) {
735  return true;
736  }
737  }
738  return false;
739  });
740  }
741  else if (child.first == "filter_adjacent") {
742  children_.emplace_back(new unit_filter_adjacent(child.second));
743  }
744  else if (child.first == "filter_location") {
745  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
746  return terrain_filter(c, args.fc, args.use_flat_tod).match(args.loc);
747  });
748  }
749  else if (child.first == "filter_side") {
750  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
751  return side_filter(c, args.fc).match(args.u.side());
752  });
753  }
754  else if ((child.first == "filter_ability") || (child.first == "experimental_filter_ability")) {
755  if(child.first == "experimental_filter_ability"){
756  deprecated_message("experimental_filter_ability", DEP_LEVEL::INDEFINITE, "", "Use filter_ability instead.");
757  }
758  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
759  if(!(c.get_parsed_config())["active"].to_bool()){
760  for(const ability_ptr& p_ab : args.u.abilities()) {
761  if(p_ab->matches_filter(c.get_parsed_config())) {
762  return true;
763  }
764  }
765  } else {
766  return specials_context_t::has_active_ability_matching_filter(args.u, args.loc, c.get_parsed_config());
767  }
768  return false;
769  });
770  }
771  else if (child.first == "has_attack") {
772  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
773  for(const attack_type& a : args.u.attacks()) {
774  if(a.matches_filter(c.get_parsed_config())) {
775  return true;
776  }
777  }
778  return false;
779  });
780  }
781  else {
782  std::stringstream errmsg;
783  errmsg << "encountered a child [" << child.first << "] of a standard unit filter, it is being ignored";
784  DBG_CF << errmsg.str();
785  }
786 
787  }
788  }
static bool is_enemy(std::size_t side, std::size_t other_side)
Definition: abilities.cpp:1041
map_location loc
Definition: move.cpp:172
bool empty() const
Definition: abilities.hpp:217
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:1881
bool is_enemy(int n) const
Definition: team.hpp:267
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: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: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:322
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:686
bool has_ability_type(const std::string &ability) const
Check if the unit has an ability of a specific type.
Definition: abilities.cpp:829
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::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...
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:147
int main(int, char **)
Definition: sdl2.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: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:1494
#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