The Battle for Wesnoth  1.19.2+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 {
203 public:
204  contains_dollar_visitor() {}
205 
206 
207  template<typename T>
208  bool operator()(const T&) const { return false; }
209 
210  bool operator()(const t_string&) const { return true; }
211 
212  bool operator()(const std::string& s) const
213  {
214  return s.find('$') != std::string::npos;
215  }
216 };
217 
218 }
219 
220 
221 unit_filter_compound::unit_filter_compound(vconfig cfg)
222  : children_()
223  , cond_children_()
224 {
225  fill(cfg);
226 }
227 
229 {
230  bool res;
231 
232  if(args.loc.valid()) {
233  scoped_xy_unit auto_store("this_unit", args.u.get_location(), args.context().get_disp_context().units());
234  if (args.u2) {
235  const map_location& loc2 = args.u2->get_location();
236  scoped_xy_unit u2_auto_store("other_unit", loc2, args.context().get_disp_context().units());
237  res = filter_impl(args);
238  } else {
239  res = filter_impl(args);
240  }
241  } else {
242  // If loc is invalid, then this is a recall list unit (already been scoped)
243  res = filter_impl(args);
244  }
245 
246  // Handle [and], [or], and [not] with in-order precedence
247  for(const auto & filter : cond_children_) {
248  switch (filter.first) {
249  case conditional_type::type::filter_and:
250  res = res && filter.second.matches(args);
251  break;
252  case conditional_type::type::filter_or:
253  res = res || filter.second.matches(args);
254  break;
255  case conditional_type::type::filter_not:
256  res = res && !filter.second.matches(args);
257  break;
258  }
259  }
260  return res;
261 }
262 
264 {
265  for(const auto & filter : children_) {
266  if (!filter->matches(args)) {
267  return false;
268  }
269  }
270  return true;
271 }
272 
273 template<typename F>
275 {
276  children_.emplace_back(new unit_filter_child_literal<F>(c, func));
277 }
278 
279 template<typename C, typename F>
281 {
282  if(v.blank()) {
283  }
284  else if(v.apply_visitor(contains_dollar_visitor())) {
285  children_.emplace_back(new unit_filter_attribute_literal<C, F>(std::move(v.str()), std::move(conv), std::move(func)));
286  }
287  else {
288  children_.emplace_back(new unit_filter_attribute_parsed<decltype(conv(v)), F>(std::move(conv(v)), std::move(func)));
289  }
290 }
291 
292 namespace {
293 
294  struct ability_match
295  {
296  std::string tag_name;
297  const config* cfg;
298  };
299 
300  void get_ability_children_id(std::vector<ability_match>& id_result,
301  const config& parent, const std::string& id) {
302  for (const config::any_child sp : parent.all_children_range())
303  {
304  if(sp.cfg["id"] == id) {
305  ability_match special = { sp.key, &sp.cfg };
306  id_result.push_back(special);
307  }
308  }
309  }
310 }
311 
313  {
314  const config& literal = cfg.get_config();
315 
316  //optimisation
317  if(literal.empty()) { return; }
318 
319  create_attribute(literal["name"],
320  [](const config::attribute_value& c) { return c.t_str(); },
321  [](const t_string& str, const unit_filter_args& args) { return str == args.u.name(); }
322  );
323 
324  create_attribute(literal["id"],
325  [](const config::attribute_value& c) { return utils::split(c.str()); },
326  [](const std::vector<std::string>& id_list, const unit_filter_args& args)
327  {
328  return std::find(id_list.begin(), id_list.end(), args.u.id()) != id_list.end();
329  }
330  );
331 
332  create_attribute(literal["type"],
333  [](const config::attribute_value& c) { return utils::split(c.str()); },
334  [](const std::vector<std::string>& types, const unit_filter_args& args)
335  {
336  return std::find(types.begin(), types.end(), args.u.type_id()) != types.end();
337  }
338  );
339 
340  create_attribute(literal["type_adv_tree"],
341  [](const config::attribute_value& c) { return utils::split(c.str()); },
342  [](const std::vector<std::string>& types, const unit_filter_args& args)
343  {
344  std::set<std::string> types_expanded;
345  for(const std::string& type : types) {
346  if(types_expanded.count(type)) {
347  continue;
348  }
349  if(const unit_type* ut = unit_types.find(type)) {
350  const auto& tree = ut->advancement_tree();
351  types_expanded.insert(tree.begin(), tree.end());
352  types_expanded.insert(type);
353  }
354  }
355  return types_expanded.find(args.u.type_id()) != types_expanded.end();
356  }
357  );
358 
359  create_attribute(literal["variation"],
360  [](const config::attribute_value& c) { return utils::split(c.str()); },
361  [](const std::vector<std::string>& types, const unit_filter_args& args)
362  {
363  return std::find(types.begin(), types.end(), args.u.variation()) != types.end();
364  }
365  );
366 
367  create_attribute(literal["has_variation"],
368  [](const config::attribute_value& c) { return utils::split(c.str()); },
369  [](const std::vector<std::string>& types, const unit_filter_args& args)
370  {
371  // If this unit is a variation itself then search in the base unit's variations.
372  const unit_type* const type = args.u.variation().empty() ? &args.u.type() : unit_types.find(args.u.type().parent_id());
373  assert(type);
374 
375  for(const std::string& variation_id : types) {
376  if (type->has_variation(variation_id)) {
377  return true;
378  }
379  }
380  return false;
381  }
382  );
383 
384  create_attribute(literal["ability"],
385  [](const config::attribute_value& c) { return utils::split(c.str()); },
386  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
387  {
388  for(const std::string& ability_id : abilities) {
389  if (args.u.has_ability_by_id(ability_id)) {
390  return true;
391  }
392  }
393  return false;
394  }
395  );
396 
397  create_attribute(literal["ability_type"],
398  [](const config::attribute_value& c) { return utils::split(c.str()); },
399  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
400  {
401  for(const std::string& ability : abilities) {
402  if (args.u.has_ability_type(ability)) {
403  return true;
404  }
405  }
406  return false;
407  }
408  );
409 
410  create_attribute(literal["ability_id_active"],
411  [](const config::attribute_value& c) { return utils::split(c.str()); },
412  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
413  {
414  assert(display::get_singleton());
415  const unit_map& units = display::get_singleton()->get_units();
416  for(const std::string& ability : abilities) {
417  std::vector<ability_match> ability_id_matches_self;
418  get_ability_children_id(ability_id_matches_self, args.u.abilities(), ability);
419  for(const ability_match& entry : ability_id_matches_self) {
420  if (args.u.get_self_ability_bool(*entry.cfg, entry.tag_name, args.loc)) {
421  return true;
422  }
423  }
424 
425  const auto adjacent = get_adjacent_tiles(args.loc);
426  for(unsigned i = 0; i < adjacent.size(); ++i) {
427  const unit_map::const_iterator it = units.find(adjacent[i]);
428  if (it == units.end() || it->incapacitated())
429  continue;
430  if (&*it == (args.u.shared_from_this()).get())
431  continue;
432 
433  std::vector<ability_match> ability_id_matches_adj;
434  get_ability_children_id(ability_id_matches_adj, it->abilities(), ability);
435  for(const ability_match& entry : ability_id_matches_adj) {
436  if (args.u.get_adj_ability_bool(*entry.cfg, entry.tag_name,i, args.loc, *it)) {
437  return true;
438  }
439  }
440  }
441  }
442  return false;
443  }
444  );
445 
446  create_attribute(literal["ability_type_active"],
447  [](const config::attribute_value& c) { return utils::split(c.str()); },
448  [](const std::vector<std::string>& abilities, const unit_filter_args& args)
449  {
450  for(const std::string& ability : abilities) {
451  if (!args.u.get_abilities(ability, args.loc).empty()) {
452  return true;
453  }
454  }
455  return false;
456  }
457  );
458 
459  create_attribute(literal["trait"],
460  [](const config::attribute_value& c)
461  {
462  auto res = utils::split(c.str());
463  std::sort(res.begin(), res.end());
464  return res;
465 
466  },
467  [](const std::vector<std::string>& check_traits, const unit_filter_args& args)
468  {
469 
470  std::vector<std::string> have_traits = args.u.get_traits_list();
471  std::vector<std::string> isect;
472  std::sort(have_traits.begin(), have_traits.end());
473  std::set_intersection(check_traits.begin(), check_traits.end(), have_traits.begin(), have_traits.end(), std::back_inserter(isect));
474  return !isect.empty();
475  }
476  );
477 
478  create_attribute(literal["race"],
479  [](const config::attribute_value& c) { return utils::split(c.str()); },
480  [](const std::vector<std::string>& races, const unit_filter_args& args)
481  {
482  return std::find(races.begin(), races.end(), args.u.race()->id()) != races.end();
483  }
484  );
485 
486  create_attribute(literal["gender"],
487  [](const config::attribute_value& c) { return string_gender(c.str()); },
488  [](unit_race::GENDER gender, const unit_filter_args& args)
489  {
490  return gender == args.u.gender();
491  }
492  );
493 
494  create_attribute(literal["upkeep"],
496  {
497  try {
498  return c.apply_visitor(unit::upkeep_parser_visitor());
499  } catch(std::invalid_argument&) {
500  return unit::upkeep_full();
501  }
502  },
503  [](unit::upkeep_t upkeep, const unit_filter_args& args)
504  {
505  return args.u.upkeep() == std::visit(unit::upkeep_value_visitor{args.u}, upkeep);
506  }
507  );
508 
509  create_attribute(literal["side"],
510  [](const config::attribute_value& c)
511  {
512  std::vector<int> res;
513  for(const std::string& s : utils::split(c.str())) {
514  try {
515  res.push_back(std::stoi(s));
516  } catch(std::invalid_argument&) {
517  WRN_CF << "ignored invalid side='" << s << "' in filter";
518  }
519  }
520  return res;
521  },
522  [](const std::vector<int>& sides, const unit_filter_args& args)
523  {
524  return std::find(sides.begin(), sides.end(), args.u.side()) != sides.end();
525  }
526  );
527 
528  create_attribute(literal["status"],
529  [](const config::attribute_value& c) { return utils::split(c.str()); },
530  [](const std::vector<std::string>& statuses, const unit_filter_args& args)
531  {
532  for(const std::string& status : statuses) {
533  if (args.u.get_state(status)) {
534  return true;
535  }
536  }
537  return false;
538  }
539  );
540 
541  create_attribute(literal["has_weapon"],
542  [](const config::attribute_value& c) { return c.str(); },
543  [](const std::string& weapon, const unit_filter_args& args)
544  {
545 
546  for(const attack_type& a : args.u.attacks()) {
547  if(a.id() == weapon) {
548  return true;
549  }
550  }
551  return false;
552  }
553  );
554 
555  create_attribute(literal["role"],
556  [](const config::attribute_value& c) { return c.str(); },
557  [](const std::string& role, const unit_filter_args& args)
558  {
559  return args.u.get_role() == role;
560  }
561  );
562 
563  create_attribute(literal["alignment"],
564  [](const config::attribute_value& c) { return c.str(); },
565  [](const std::string& alignment, const unit_filter_args& args)
566  {
567  return unit_alignments::get_string(args.u.alignment()) == alignment;
568  }
569  );
570 
571  create_attribute(literal["ai_special"],
572  [](const config::attribute_value& c) { return c.str(); },
573  [](const std::string& ai_special, const unit_filter_args& args)
574  {
575  return (ai_special == "guardian") == args.u.get_state(unit::STATE_GUARDIAN);
576  }
577  );
578 
579  create_attribute(literal["usage"],
580  [](const config::attribute_value& c) { return utils::split(c.str()); },
581  [](const std::vector<std::string>& usages, const unit_filter_args& args)
582  {
583  for(const std::string& usage : usages) {
584  if(args.u.usage() == usage) {
585  return true;
586  }
587  }
588  return false;
589  }
590  );
591 
592  create_attribute(literal["canrecruit"],
593  [](const config::attribute_value& c) { return c.to_bool(); },
594  [](bool canrecruit, const unit_filter_args& args)
595  {
596  return args.u.can_recruit() == canrecruit;
597  }
598  );
599 
600  create_attribute(literal["recall_cost"],
601  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
602  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
603  {
604  for(auto cost : ranges) {
605  if(cost.first <= args.u.recall_cost() && args.u.recall_cost() <= cost.second) {
606  return true;
607  }
608  }
609  return false;
610  }
611  );
612 
613  create_attribute(literal["level"],
614  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
615  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
616  {
617  for(auto lvl : ranges) {
618  if(lvl.first <= args.u.level() && args.u.level() <= lvl.second) {
619  return true;
620  }
621  }
622  return false;
623  }
624  );
625 
626  create_attribute(literal["defense"],
627  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
628  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
629  {
630  int actual_defense = args.u.defense_modifier(args.context().get_disp_context().map().get_terrain(args.loc));
631  for(auto def : ranges) {
632  if(def.first <= actual_defense && actual_defense <= def.second) {
633  return true;
634  }
635  }
636  return false;
637  }
638  );
639 
640  create_attribute(literal["movement_cost"],
641  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
642  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
643  {
644  int actual_cost = args.u.movement_cost(args.context().get_disp_context().map().get_terrain(args.loc));
645  for(auto cost : ranges) {
646  if(cost.first <= actual_cost && actual_cost <= cost.second) {
647  return true;
648  }
649  }
650  return false;
651  }
652  );
653 
654  create_attribute(literal["vision_cost"],
655  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
656  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
657  {
658  int actual_cost = args.u.vision_cost(args.context().get_disp_context().map().get_terrain(args.loc));
659  for(auto cost : ranges) {
660  if(cost.first <= actual_cost && actual_cost <= cost.second) {
661  return true;
662  }
663  }
664  return false;
665  }
666  );
667 
668  create_attribute(literal["jamming_cost"],
669  [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
670  [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
671  {
672  int actual_cost = args.u.jamming_cost(args.context().get_disp_context().map().get_terrain(args.loc));
673  for(auto cost : ranges) {
674  if(cost.first <= actual_cost && actual_cost <= cost.second) {
675  return true;
676  }
677  }
678  return false;
679  }
680  );
681 
682  create_attribute(literal["lua_function"],
683  [](const config::attribute_value& c) { return c.str(); },
684  [](const std::string& lua_function, const unit_filter_args& args)
685  {
686  if (game_lua_kernel * lk = args.context().get_lua_kernel()) {
687  return lk->run_filter(lua_function.c_str(), args.u);
688  }
689  return true;
690  }
691  );
692 
693  create_attribute(literal["formula"],
694  [](const config::attribute_value& c)
695  {
696  //TODO: catch syntax error.
698  },
699  [](const wfl::formula& form, const unit_filter_args& args)
700  {
701  try {
702  const wfl::unit_callable main(args.loc, args.u);
703  wfl::map_formula_callable callable(main.fake_ptr());
704  if (args.u2) {
705  auto secondary = std::make_shared<wfl::unit_callable>(*args.u2);
706  callable.add("other", wfl::variant(secondary));
707  // It's not destroyed upon scope exit because the variant holds a reference
708  }
709  if(!form.evaluate(callable).as_bool()) {
710  return false;
711  }
712  return true;
713  } catch(const wfl::formula_error& e) {
714  lg::log_to_chat() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
715  ERR_WML << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")";
716  // Formulae with syntax errors match nothing
717  return false;
718  }
719  }
720  );
721 
722  create_attribute(literal["find_in"],
723  [](const config::attribute_value& c) { return c.str(); },
724  [](const std::string& find_in, const unit_filter_args& args)
725  {
726  // Allow filtering by searching a stored variable of units
727  if (const game_data * gd = args.context().get_game_data()) {
728  try
729  {
730  for (const config& c : gd->get_variable_access_read(find_in).as_array())
731  {
732  if(c["id"] == args.u.id()) {
733  return true;
734  }
735  }
736  return false;
737  }
738  catch(const invalid_variablename_exception&)
739  {
740  return false;
741  }
742  }
743  return true;
744  }
745  );
746 
747  if (!literal["x"].blank() || !literal["y"].blank()) {
748  children_.emplace_back(new unit_filter_xy(literal["x"], literal["y"]));
749  }
750 
751  for(auto child : cfg.all_ordered()) {
752  auto cond = conditional_type::get_enum(child.first);
753  if(cond) {
754  cond_children_.emplace_back(std::piecewise_construct_t(), std::tuple(*cond), std::tuple(child.second));
755  }
756  else if (child.first == "filter_wml") {
757  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
758  config fwml = c.get_parsed_config();
759 
760  /* Check if the filter only cares about variables.
761  If so, no need to serialize the whole unit. */
762  config::all_children_itors ci = fwml.all_children_range();
763  if (fwml.all_children_count() == 1 && fwml.attribute_count() == 1 && ci.front().key == "variables") {
764  return args.u.variables().matches(ci.front().cfg);
765  } else {
766  config ucfg;
767  args.u.write(ucfg);
768  return ucfg.matches(fwml);
769  }
770  });
771  }
772  else if (child.first == "filter_vision") {
773  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
774  std::set<int> viewers;
775  side_filter ssf(c, args.fc);
776  std::vector<int> sides = ssf.get_teams();
777  viewers.insert(sides.begin(), sides.end());
778 
779  for (const int viewer : viewers) {
780  bool fogged = args.context().get_disp_context().get_team(viewer).fogged(args.loc);
781  // Check is_enemy() before invisible() to prevent infinite recursion in [abilities][hides][filter_self][filter_vision]
782  bool hiding = args.context().get_disp_context().get_team(viewer).is_enemy(args.u.side()) && args.u.invisible(args.loc);
783  bool unit_hidden = fogged || hiding;
784  if (c["visible"].to_bool(true) != unit_hidden) {
785  return true;
786  }
787  }
788  return false;
789  });
790  }
791  else if (child.first == "filter_adjacent") {
792  children_.emplace_back(new unit_filter_adjacent(child.second));
793  }
794  else if (child.first == "filter_location") {
795  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
796  return terrain_filter(c, args.fc, args.use_flat_tod).match(args.loc);
797  });
798  }
799  else if (child.first == "filter_side") {
800  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
801  return side_filter(c, args.fc).match(args.u.side());
802  });
803  }
804  else if (child.first == "experimental_filter_ability") {
805  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
806  for(const config::any_child ab : args.u.abilities().all_children_range()) {
807  if(args.u.ability_matches_filter(ab.cfg, ab.key, c.get_parsed_config())) {
808  return true;
809  }
810  }
811  return false;
812  });
813  }
814  else if (child.first == "experimental_filter_ability_active") {
815  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
816  if(!display::get_singleton()){
817  return false;
818  }
819  const unit_map& units = display::get_singleton()->get_units();
820  for(const config::any_child ab : args.u.abilities().all_children_range()) {
821  if(args.u.ability_matches_filter(ab.cfg, ab.key, c.get_parsed_config())) {
822  if (args.u.get_self_ability_bool(ab.cfg, ab.key, args.loc)) {
823  return true;
824  }
825  }
826  }
827 
828  const auto adjacent = get_adjacent_tiles(args.loc);
829  for(unsigned i = 0; i < adjacent.size(); ++i) {
830  const unit_map::const_iterator it = units.find(adjacent[i]);
831  if (it == units.end() || it->incapacitated())
832  continue;
833  if (&*it == (args.u.shared_from_this()).get())
834  continue;
835 
836  for(const config::any_child ab : it->abilities().all_children_range()) {
837  if(it->ability_matches_filter(ab.cfg, ab.key, c.get_parsed_config())) {
838  if (args.u.get_adj_ability_bool(ab.cfg, ab.key, i, args.loc, *it)) {
839  return true;
840  }
841  }
842  }
843  }
844  return false;
845  });
846  }
847  else if (child.first == "has_attack") {
848  create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
849  for(const attack_type& a : args.u.attacks()) {
850  if(a.matches_filter(c.get_parsed_config())) {
851  return true;
852  }
853  }
854  return false;
855  });
856  }
857  else {
858  std::stringstream errmsg;
859  errmsg << "encountered a child [" << child.first << "] of a standard unit filter, it is being ignored";
860  DBG_CF << errmsg.str();
861  }
862 
863  }
864  }
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:1224
Visitor helper class to fetch the appropriate upkeep value.
Definition: unit.hpp:1180
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:36
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
map_display and display: classes which take care of displaying the map and game-data on the screen.
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:1569
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:1791
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:1564
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:2012
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:1796
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:1389
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
Definition: unit.hpp:1472
int vision_cost(const t_translation::terrain_code &terrain) const
Get the unit's vision cost on a particular terrain.
Definition: unit.hpp:1482
int jamming_cost(const t_translation::terrain_code &terrain) const
Get the unit's jamming cost on a particular terrain.
Definition: unit.hpp:1492
std::variant< upkeep_full, upkeep_loyal, int > upkeep_t
Definition: unit.hpp:1176
int upkeep() const
Gets the amount of gold this unit costs a side per turn.
Definition: unit.cpp:1770
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:554
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:263
void create_child(const vconfig &c, F func)
Definition: filter.cpp:274
void create_attribute(const config::attribute_value c, C conv, F func)
Definition: filter.cpp:280
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:228
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