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