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