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