The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
filter.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2014 - 2025
3 by Chris Beck <render787@gmail.com>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
16#include "units/filter.hpp"
17
18#include "log.hpp"
19
20#include "display_context.hpp"
21#include "config.hpp"
22#include "game_data.hpp"
23#include "game_version.hpp" // for version_info
24#include "map/map.hpp"
25#include "map/location.hpp"
26#include "scripting/game_lua_kernel.hpp" //Needed for lua kernel
27#include "side_filter.hpp"
28#include "team.hpp"
29#include "terrain/filter.hpp"
30#include "units/unit.hpp"
31#include "units/types.hpp"
32#include "variable.hpp" // needed for vconfig, scoped unit
34#include "formula/formula.hpp"
37#include "resources.hpp"
38#include "deprecation.hpp"
39#include "utils/general.hpp"
40
41static 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
46static lg::log_domain log_wml("wml");
47#define ERR_WML LOG_STREAM(err, log_wml)
48
49using 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
63
64bool unit_filter::matches(const unit & u, const unit & u2) const {
66}
67
68std::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
95namespace {
96
97struct 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
121struct 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 std::size_t radius = cfg_["radius"].to_int(1);
135
136 config::attribute_value i_adjacent = cfg_["adjacent"];
137 std::vector<map_location::direction> dirs;
138 for(const unit& u : units) {
139 const map_location& from_loc = u.get_location();
140 std::size_t distance = distance_between(from_loc, args.loc);
141 if(u.underlying_id() == args.u.underlying_id() || distance > radius || !child_.matches(unit_filter_args{u, from_loc, &args.u, args.fc, args.use_flat_tod} )) {
142 continue;
143 }
144 int dir = 0;
145 for(unsigned j = 0; j < adjacent.size(); ++j) {
146 bool adj_or_dist = distance != 1 ? distance_between(adjacent[j], from_loc) == (distance - 1) : adjacent[j] == from_loc;
147 if(adj_or_dist) {
148 dir = j;
149 break;
150 }
151 }
152 assert(dir >= 0 && dir <= 5);
153 map_location::direction direction{ dir };
154 if(!i_adjacent.empty()) { //key adjacent defined
155 if(!utils::contains(map_location::parse_directions(i_adjacent), direction)) {
156 continue;
157 }
158 }
159 auto is_enemy = cfg_["is_enemy"];
160 if (!is_enemy.empty() && is_enemy.to_bool() != args.context().get_disp_context().get_team(args.u.side()).is_enemy(u.side())) {
161 continue;
162 }
163 ++match_count;
164 }
165
166 static std::vector<std::pair<int,int>> default_counts = {{{1, std::numeric_limits<int>::max()}}};
167 config::attribute_value i_count = cfg_["count"];
168 return in_ranges(match_count, !i_count.empty() ? utils::parse_ranges_unsigned(i_count) : default_counts);
169 }
170
171 const unit_filter_compound child_;
172 const vconfig cfg_;
173};
174
175
176template<typename F>
177struct unit_filter_child_literal : public unit_filter_base
178{
179 unit_filter_child_literal(const vconfig& v, const F& f) : v_(v) , f_(f) {}
180 virtual bool matches(const unit_filter_args& args) const override
181 {
182 return f_(v_, args);
183 }
184 vconfig v_;
185 F f_;
186};
187
188template<typename T, typename F>
189struct unit_filter_attribute_parsed : public unit_filter_base
190{
191 unit_filter_attribute_parsed(T&& v, F&& f) : v_(std::move(v)), f_(std::move(f)) {}
192 virtual bool matches(const unit_filter_args& args) const override
193 {
194 return f_(v_, args);
195 }
196 T v_;
197 F f_;
198};
199
200template<typename C, typename F>
201struct unit_filter_attribute_literal : public unit_filter_base
202{
203 unit_filter_attribute_literal(std::string&& v, C&& c, F&& f) : v_(std::move(v)), c_(std::move(c)), f_(std::move(f)) {}
204 virtual bool matches(const unit_filter_args& args) const override
205 {
208 return f_(c_(v), args);
209 }
210 std::string v_;
211 C c_;
212 F f_;
213};
214
215class contains_dollar_visitor
216#ifdef USING_BOOST_VARIANT
217 : public boost::static_visitor<bool>
218#endif
219{
220public:
221 contains_dollar_visitor() {}
222
223
224 template<typename T>
225 bool operator()(const T&) const { return false; }
226
227 bool operator()(const t_string&) const { return true; }
228
229 bool operator()(const std::string& s) const
230 {
231 return s.find('$') != std::string::npos;
232 }
233};
234
235}
236
237
239 : children_()
240 , cond_children_()
241{
242 fill(cfg);
243}
244
246{
247 bool res;
248
249 if(args.loc.valid()) {
250 scoped_xy_unit auto_store("this_unit", args.u.get_location(), args.context().get_disp_context().units());
251 if (args.u2) {
252 const map_location& loc2 = args.u2->get_location();
253 scoped_xy_unit u2_auto_store("other_unit", loc2, args.context().get_disp_context().units());
254 res = filter_impl(args);
255 } else {
256 res = filter_impl(args);
257 }
258 } else {
259 // If loc is invalid, then this is a recall list unit (already been scoped)
260 res = filter_impl(args);
261 }
262
263 // Handle [and], [or], and [not] with in-order precedence
264 for(const auto & filter : cond_children_) {
265 switch (filter.first) {
266 case conditional_type::type::filter_and:
267 res = res && filter.second.matches(args);
268 break;
269 case conditional_type::type::filter_or:
270 res = res || filter.second.matches(args);
271 break;
272 case conditional_type::type::filter_not:
273 res = res && !filter.second.matches(args);
274 break;
275 }
276 }
277 return res;
278}
279
281{
282 for(const auto & filter : children_) {
283 if (!filter->matches(args)) {
284 return false;
285 }
286 }
287 return true;
288}
289
290template<typename F>
292{
293 children_.emplace_back(new unit_filter_child_literal<F>(c, func));
294}
295
296template<typename C, typename F>
298{
299 if(v.blank()) {
300 }
301 else if(v.apply_visitor(contains_dollar_visitor())) {
302 children_.emplace_back(new unit_filter_attribute_literal<C, F>(std::move(v.str()), std::move(conv), std::move(func)));
303 }
304 else {
305 children_.emplace_back(new unit_filter_attribute_parsed<decltype(conv(v)), F>(std::move(conv(v)), std::move(func)));
306 }
307}
308
310 {
311 const config& literal = cfg.get_config();
312
313 //optimisation
314 if(literal.empty()) { return; }
315
316 create_attribute(literal["name"],
317 [](const config::attribute_value& c) { return c.t_str(); },
318 [](const t_string& str, const unit_filter_args& args) { return str == args.u.name(); }
319 );
320
321 create_attribute(literal["id"],
322 [](const config::attribute_value& c) { return utils::split(c.str()); },
323 [](const std::vector<std::string>& id_list, const unit_filter_args& args)
324 {
325 return utils::contains(id_list, args.u.id());
326 }
327 );
328
329 create_attribute(literal["type"],
330 [](const config::attribute_value& c) { return utils::split(c.str()); },
331 [](const std::vector<std::string>& types, const unit_filter_args& args)
332 {
333 return utils::contains(types, args.u.type_id());
334 }
335 );
336
337 create_attribute(literal["type_adv_tree"],
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 std::set<std::string> types_expanded;
342 for(const std::string& type : types) {
343 if(types_expanded.count(type)) {
344 continue;
345 }
346 if(const unit_type* ut = unit_types.find(type)) {
347 const auto& tree = ut->advancement_tree();
348 types_expanded.insert(tree.begin(), tree.end());
349 types_expanded.insert(type);
350 }
351 }
352 return utils::contains(types_expanded, args.u.type_id());
353 }
354 );
355
356 create_attribute(literal["variation"],
357 [](const config::attribute_value& c) { return utils::split(c.str()); },
358 [](const std::vector<std::string>& types, const unit_filter_args& args)
359 {
360 return utils::contains(types, args.u.variation());
361 }
362 );
363
364 create_attribute(literal["has_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 // If this unit is a variation itself then search in the base unit's variations.
369 const unit_type* const type = args.u.variation().empty() ? &args.u.type() : unit_types.find(args.u.type().parent_id());
370 assert(type);
371
372 for(const std::string& variation_id : types) {
373 if (type->has_variation(variation_id)) {
374 return true;
375 }
376 }
377 return false;
378 }
379 );
380
381 create_attribute(literal["ability"],
382 [](const config::attribute_value& c) { return utils::split(c.str()); },
383 [](const std::vector<std::string>& abilities, const unit_filter_args& args)
384 {
385 for(const std::string& ability_id : abilities) {
386 if (args.u.has_ability_by_id(ability_id)) {
387 return true;
388 }
389 }
390 return false;
391 }
392 );
393
394 create_attribute(literal["ability_type"],
395 [](const config::attribute_value& c) { return utils::split(c.str()); },
396 [](const std::vector<std::string>& abilities, const unit_filter_args& args)
397 {
398 for(const std::string& ability : abilities) {
399 if (args.u.has_ability_type(ability)) {
400 return true;
401 }
402 }
403 return false;
404 }
405 );
406
407 create_attribute(literal["ability_id_active"],
408 [](const config::attribute_value& c) { return utils::split_set(c.str()); },
409 [](const std::set<std::string>& abilities, const unit_filter_args& args)
410 {
411 return utils::find_if(abilities, [&](const std::string& abilitiy_id) { return specials_context_t::has_active_ability_id(args.u, args.loc, abilitiy_id); });
412 }
413 );
414
415 create_attribute(literal["ability_type_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 for(const std::string& ability : abilities) {
420 if (!args.u.get_abilities(ability, args.loc).empty()) {
421 return true;
422 }
423 }
424 return false;
425 }
426 );
427
428 create_attribute(literal["trait"],
429 [](const config::attribute_value& c)
430 {
431 auto res = utils::split(c.str());
432 std::sort(res.begin(), res.end());
433 return res;
434
435 },
436 [](const std::vector<std::string>& check_traits, const unit_filter_args& args)
437 {
438
439 std::vector<std::string> have_traits = args.u.get_traits_list();
440 std::vector<std::string> isect;
441 std::sort(have_traits.begin(), have_traits.end());
442 std::set_intersection(check_traits.begin(), check_traits.end(), have_traits.begin(), have_traits.end(), std::back_inserter(isect));
443 return !isect.empty();
444 }
445 );
446
447 create_attribute(literal["race"],
448 [](const config::attribute_value& c) { return utils::split(c.str()); },
449 [](const std::vector<std::string>& races, const unit_filter_args& args)
450 {
451 return utils::contains(races, args.u.race()->id());
452 }
453 );
454
455 create_attribute(literal["gender"],
456 [](const config::attribute_value& c) { return string_gender(c.str()); },
457 [](unit_race::GENDER gender, const unit_filter_args& args)
458 {
459 return gender == args.u.gender();
460 }
461 );
462
463 create_attribute(literal["upkeep"],
465 {
466 try {
467 return c.apply_visitor(unit::upkeep_parser_visitor());
468 } catch(std::invalid_argument&) {
469 return unit::upkeep_full();
470 }
471 },
472 [](unit::upkeep_t upkeep, const unit_filter_args& args)
473 {
474 return args.u.upkeep() == utils::visit(unit::upkeep_value_visitor{args.u}, upkeep);
475 }
476 );
477
478 create_attribute(literal["side"],
479 [](const config::attribute_value& c)
480 {
481 std::vector<int> res;
482 for(const std::string& s : utils::split(c.str())) {
483 try {
484 res.push_back(std::stoi(s));
485 } catch(std::invalid_argument&) {
486 WRN_CF << "ignored invalid side='" << s << "' in filter";
487 }
488 }
489 return res;
490 },
491 [](const std::vector<int>& sides, const unit_filter_args& args)
492 {
493 return utils::contains(sides, args.u.side());
494 }
495 );
496
497 create_attribute(literal["status"],
498 [](const config::attribute_value& c) { return utils::split(c.str()); },
499 [](const std::vector<std::string>& statuses, const unit_filter_args& args)
500 {
501 for(const std::string& status : statuses) {
502 if (args.u.get_state(status)) {
503 return true;
504 }
505 }
506 return false;
507 }
508 );
509
510 create_attribute(literal["has_weapon"],
511 [](const config::attribute_value& c) { return c.str(); },
512 [](const std::string& weapon, const unit_filter_args& args)
513 {
514
515 for(const attack_type& a : args.u.attacks()) {
516 if(a.id() == weapon) {
517 return true;
518 }
519 }
520 return false;
521 }
522 );
523
524 create_attribute(literal["role"],
525 [](const config::attribute_value& c) { return c.str(); },
526 [](const std::string& role, const unit_filter_args& args)
527 {
528 return args.u.get_role() == role;
529 }
530 );
531
532 create_attribute(literal["alignment"],
533 [](const config::attribute_value& c) { return c.str(); },
534 [](const std::string& alignment, const unit_filter_args& args)
535 {
536 return unit_alignments::get_string(args.u.alignment()) == alignment;
537 }
538 );
539
540 create_attribute(literal["ai_special"],
541 [](const config::attribute_value& c) { return c.str(); },
542 [](const std::string& ai_special, const unit_filter_args& args)
543 {
544 return (ai_special == "guardian") == args.u.get_state(unit::STATE_GUARDIAN);
545 }
546 );
547
548 create_attribute(literal["usage"],
549 [](const config::attribute_value& c) { return utils::split(c.str()); },
550 [](const std::vector<std::string>& usages, const unit_filter_args& args)
551 {
552 for(const std::string& usage : usages) {
553 if(args.u.usage() == usage) {
554 return true;
555 }
556 }
557 return false;
558 }
559 );
560
561 create_attribute(literal["canrecruit"],
562 [](const config::attribute_value& c) { return c.to_bool(); },
563 [](bool canrecruit, const unit_filter_args& args)
564 {
565 return args.u.can_recruit() == canrecruit;
566 }
567 );
568
569 create_attribute(literal["recall_cost"],
570 [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
571 [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
572 {
573 return in_ranges(args.u.recall_cost(), ranges);
574 }
575 );
576
577 create_attribute(literal["level"],
578 [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
579 [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
580 {
581 return in_ranges(args.u.level(), ranges);
582 }
583 );
584
585 create_attribute(literal["defense"],
586 [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
587 [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
588 {
589 int actual_defense = args.u.defense_modifier(args.context().get_disp_context().map().get_terrain(args.loc));
590 return in_ranges(actual_defense, ranges);
591 }
592 );
593
594 create_attribute(literal["movement_cost"],
595 [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
596 [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
597 {
598 int actual_cost = args.u.movement_cost(args.context().get_disp_context().map().get_terrain(args.loc));
599 return in_ranges(actual_cost, ranges);
600 }
601 );
602
603 create_attribute(literal["vision_cost"],
604 [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
605 [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
606 {
607 int actual_cost = args.u.vision_cost(args.context().get_disp_context().map().get_terrain(args.loc));
608 return in_ranges(actual_cost, ranges);
609 }
610 );
611
612 create_attribute(literal["jamming_cost"],
613 [](const config::attribute_value& c) { return utils::parse_ranges_unsigned(c.str()); },
614 [](const std::vector<std::pair<int,int>>& ranges, const unit_filter_args& args)
615 {
616 int actual_cost = args.u.jamming_cost(args.context().get_disp_context().map().get_terrain(args.loc));
617 return in_ranges(actual_cost, ranges);
618 }
619 );
620
621 create_attribute(literal["lua_function"],
622 [](const config::attribute_value& c) { return c.str(); },
623 [](const std::string& lua_function, const unit_filter_args& args)
624 {
625 if (game_lua_kernel * lk = args.context().get_lua_kernel()) {
626 return lk->run_filter(lua_function.c_str(), args.u);
627 }
628 return true;
629 }
630 );
631
632 create_attribute(literal["formula"],
633 [](const config::attribute_value& c)
634 {
635 try {
637 } catch(const wfl::formula_error& e) {
638 lg::log_to_chat() << "Formula error while evaluating formula in unit filter: " << e.type << " at "
639 << e.filename << ':' << e.line << ")\n";
640 ERR_WML << "Formula error while evaluating formula in unit filter: " << e.type << " at "
641 << e.filename << ':' << e.line << ")";
642 return wfl::formula("");
643 }
644 },
645 [](const wfl::formula& form, const unit_filter_args& args)
646 {
647 try {
648 const wfl::unit_callable main(args.loc, args.u);
649 wfl::map_formula_callable callable(main.fake_ptr());
650 if (args.u2) {
651 auto secondary = std::make_shared<wfl::unit_callable>(*args.u2);
652 callable.add("other", wfl::variant(secondary));
653 // It's not destroyed upon scope exit because the variant holds a reference
654 }
655 if(!form.evaluate(callable).as_bool()) {
656 return false;
657 }
658 return true;
659 } catch(const wfl::formula_error& e) {
660 lg::log_to_chat() << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
661 ERR_WML << "Formula error in unit filter: " << e.type << " at " << e.filename << ':' << e.line << ")";
662 // Formulae with syntax errors match nothing
663 return false;
664 }
665 }
666 );
667
668 create_attribute(literal["find_in"],
669 [](const config::attribute_value& c) { return c.str(); },
670 [](const std::string& find_in, const unit_filter_args& args)
671 {
672 // Allow filtering by searching a stored variable of units
673 if (const game_data * gd = args.context().get_game_data()) {
674 try
675 {
676 for (const config& c : gd->get_variable_access_read(find_in).as_array())
677 {
678 if(c["id"] == args.u.id()) {
679 return true;
680 }
681 }
682 return false;
683 }
685 {
686 return false;
687 }
688 }
689 return true;
690 }
691 );
692
693 if (!literal["x"].blank() || !literal["y"].blank()) {
694 children_.emplace_back(new unit_filter_xy(literal["x"], literal["y"]));
695 }
696
697 for(auto child : cfg.all_ordered()) {
698 auto cond = conditional_type::get_enum(child.first);
699 if(cond) {
700 cond_children_.emplace_back(std::piecewise_construct_t(), std::tuple(*cond), std::tuple(child.second));
701 }
702 else if (child.first == "filter_wml") {
703 create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
704 config fwml = c.get_parsed_config();
705
706 /* Check if the filter only cares about variables.
707 If so, no need to serialize the whole unit. */
708 config::all_children_itors ci = fwml.all_children_range();
709 if (fwml.all_children_count() == 1 && fwml.attribute_count() == 0 && ci.front().key == "variables") {
710 return args.u.variables().matches(ci.front().cfg);
711 } else {
712 config ucfg;
713 args.u.write(ucfg);
714 return ucfg.matches(fwml);
715 }
716 });
717 }
718 else if (child.first == "filter_vision") {
719 create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
720 std::set<int> viewers;
721 side_filter ssf(c, args.fc);
722 std::vector<int> sides = ssf.get_teams();
723 viewers.insert(sides.begin(), sides.end());
724
725 for (const int viewer : viewers) {
726 bool fogged = args.context().get_disp_context().get_team(viewer).fogged(args.loc);
727 // Check is_enemy() before invisible() to prevent infinite recursion in [abilities][hides][filter_self][filter_vision]
728 bool hiding = args.context().get_disp_context().get_team(viewer).is_enemy(args.u.side()) && args.u.invisible(args.loc);
729 bool unit_hidden = fogged || hiding;
730 if (c["visible"].to_bool(true) != unit_hidden) {
731 return true;
732 }
733 }
734 return false;
735 });
736 }
737 else if (child.first == "filter_adjacent") {
738 children_.emplace_back(new unit_filter_adjacent(child.second));
739 }
740 else if (child.first == "filter_location") {
741 create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
742 return terrain_filter(c, args.fc, args.use_flat_tod).match(args.loc);
743 });
744 }
745 else if (child.first == "filter_side") {
746 create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
747 return side_filter(c, args.fc).match(args.u.side());
748 });
749 }
750 else if ((child.first == "filter_ability") || (child.first == "experimental_filter_ability")) {
751 if(child.first == "experimental_filter_ability"){
752 deprecated_message("experimental_filter_ability", DEP_LEVEL::FOR_REMOVAL, {1, 21, 0}, "Use filter_ability instead.");
753 }
754 create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
755 if(!(c.get_parsed_config())["active"].to_bool()){
756 for(const ability_ptr& p_ab : args.u.abilities()) {
757 if(p_ab->matches_filter(c.get_parsed_config())) {
758 return true;
759 }
760 }
761 } else {
762 return specials_context_t::has_active_ability_matching_filter(args.u, args.loc, c.get_parsed_config());
763 }
764 return false;
765 });
766 }
767 else if (child.first == "has_attack") {
768 create_child(child.second, [](const vconfig& c, const unit_filter_args& args) {
769 for(const attack_type& a : args.u.attacks()) {
770 if(a.matches_filter(c.get_parsed_config())) {
771 return true;
772 }
773 }
774 return false;
775 });
776 }
777 else {
778 std::stringstream errmsg;
779 errmsg << "encountered a child [" << child.first << "] of a standard unit filter, it is being ignored";
780 DBG_CF << errmsg.str();
781 }
782
783 }
784 }
static bool is_enemy(std::size_t side, std::size_t other_side)
map_location loc
Definition move.cpp:172
bool empty() const
Variant for storing WML attributes.
std::string str(const std::string &fallback="") const
auto apply_visitor(const V &visitor) const
Visitor support: Applies a visitor to the underlying variant.
bool blank() const
Tests for an attribute that was never set.
bool empty() const
Tests for an attribute that either was never set or was set to "".
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
bool empty() const
Definition config.cpp:823
virtual const gamemap & map() const =0
const team & get_team(int side) const
This getter takes a 1-based side number, not a 0-based team number.
virtual const unit_map & units() const =0
virtual const game_data * get_game_data() const =0
virtual game_lua_kernel * get_lua_kernel() const =0
virtual const display_context & get_disp_context() const =0
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition map.cpp:265
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition map.cpp:347
static bool has_active_ability_id(const unit &un, map_location loc, const std::string &id)
bool is_enemy(int n) const
Definition team.hpp:270
Visitor helper class to parse the upkeep value from a config.
Definition unit.hpp:1131
Visitor helper class to fetch the appropriate upkeep value.
Definition unit.hpp:1081
unit_filter(const vconfig &cfg)
Definition filter.cpp:51
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:68
unit_const_ptr first_match_on_map() const
Definition filter.cpp:85
unit_filter_impl::unit_filter_compound impl_
Definition filter.hpp:185
Container associating units to locations.
Definition map.hpp:98
unit_iterator end()
Definition map.hpp:428
unit_iterator begin()
Definition map.hpp:418
const std::string & id() const
Definition race.hpp:35
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition types.cpp:1253
A single unit type that the player may recruit.
Definition types.hpp:43
const std::string & parent_id() const
The id of the original type from which this (variation) descended.
Definition types.hpp:149
This class represents a single unit of a specific type.
Definition unit.hpp:39
A variable-expanding proxy for the config class.
Definition variable.hpp:45
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition formula.hpp:48
map_formula_callable & add(const std::string &key, const variant &value)
Definition callable.hpp:249
bool as_bool() const
Returns a boolean state of the variant value.
Definition variant.cpp:357
#define DBG_CF
Definition config.cpp:38
Definitions for the interface to Wesnoth Markup Language (WML).
#define ERR_WML
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
const config * cfg
Interfaces for manipulating version numbers of engine, add-ons, etc.
active_ability_list get_abilities(const std::string &tag_name, const map_location &loc) const
Gets the unit's active abilities of a particular type if it were on a specified location.
bool has_ability_type(const std::string &ability) const
Check if the unit has an ability of a specific type.
bool has_ability_by_id(const std::string &ability) const
Check if the unit has an ability of a specific ID.
Definition unit.cpp:1528
unit_alignments::type alignment() const
The alignment of this unit.
Definition unit.hpp:397
int level() const
The current level of this unit.
Definition unit.hpp:481
std::string usage() const
Gets this unit's usage.
Definition unit.hpp:608
const unit_type & type() const
This unit's type, accounting for gender and variation.
Definition unit.hpp:261
int recall_cost() const
How much gold it costs to recall this unit, or -1 if the side's default recall cost is used.
Definition unit.hpp:562
bool get_state(const std::string &state) const
Check if the unit is affected by a status effect.
Definition unit.cpp:1446
const std::string & type_id() const
The id of this unit's type.
Definition unit.cpp:1964
const std::string & get_role() const
Gets this unit's role.
Definition unit.hpp:591
const unit_race * race() const
Gets this unit's race.
Definition unit.hpp:415
bool can_recruit() const
Whether this unit can recruit other units - ie, are they a leader unit.
Definition unit.hpp:534
const std::string & id() const
Gets this unit's id.
Definition unit.hpp:286
int side() const
The side this unit belongs to.
Definition unit.hpp:249
const std::string & variation() const
The ID of the variation of this unit's type.
Definition unit.hpp:494
std::size_t underlying_id() const
This unit's unique internal ID.
Definition unit.hpp:298
unit_race::GENDER gender() const
The gender of this unit.
Definition unit.hpp:387
const t_string & name() const
Gets this unit's translatable display name.
Definition unit.hpp:309
@ STATE_GUARDIAN
The unit cannot be healed.
Definition unit.hpp:781
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition unit.hpp:848
int defense_modifier(const t_translation::terrain_code &terrain, const map_location &loc) const
The unit's defense on a given terrain.
Definition unit.cpp:1777
const map_location & get_location() const
The current map location this unit is at.
Definition unit.hpp:1327
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
Definition unit.hpp:1410
int vision_cost(const t_translation::terrain_code &terrain) const
Get the unit's vision cost on a particular terrain.
Definition unit.hpp:1420
int jamming_cost(const t_translation::terrain_code &terrain) const
Get the unit's jamming cost on a particular terrain.
Definition unit.hpp:1430
int upkeep() const
Gets the amount of gold this unit costs a side per turn.
Definition unit.cpp:1751
std::vector< std::string > get_traits_list() const
Gets a list of the traits this unit currently has, including hidden traits.
Definition unit.hpp:1027
utils::variant< upkeep_full, upkeep_loyal, int > upkeep_t
Definition unit.hpp:1074
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition location.cpp:584
void get_adjacent_tiles(const map_location &a, utils::span< map_location, 6 > res)
Function which, given a location, will place all adjacent locations in res.
Definition location.cpp:513
Standard logging facilities (interface).
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp > > &ranges)
Definition math.hpp:85
#define WRN_CF
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:550
game_data * gamedata
Definition resources.cpp:22
STL namespace.
auto * find_if(Container &container, const Predicate &predicate)
Convenience wrapper for using find_if on a container without needing to comare to end()
Definition general.hpp:152
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
std::set< std::string > split_set(std::string_view s, char sep, const int flags)
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition general.hpp:88
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:149
int main(int, char **)
Definition sdl3.cpp:25
Encapsulates the map of the game.
Definition location.hpp:46
static std::vector< direction > parse_directions(const std::string &str)
Parse_directions takes a comma-separated list, and filters out any invalid directions.
Definition location.cpp:138
bool valid() const
Definition location.hpp:111
direction
Valid directions which can be moved in our hexagonal world.
Definition location.hpp:48
bool matches_range(const std::string &xloc, const std::string &yloc) const
Definition location.cpp:320
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition enum_base.hpp:46
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition enum_base.hpp:57
const filter_context & context() const
Definition filter.hpp:62
virtual bool matches(const unit_filter_args &) const =0
bool filter_impl(const unit_filter_args &u) const
Definition filter.cpp:280
void fill(const vconfig &cfg)
Definition filter.cpp:309
void create_child(const vconfig &c, F func)
Definition filter.cpp:291
void create_attribute(const config::attribute_value &c, C conv, F func)
Definition filter.cpp:297
std::vector< std::pair< conditional_type::type, unit_filter_compound > > cond_children_
Definition filter.hpp:97
unit_filter_compound(const vconfig &cfg)
Definition filter.cpp:238
virtual bool matches(const unit_filter_args &u) const override
Definition filter.cpp:245
std::vector< std::shared_ptr< unit_filter_base > > children_
Definition filter.hpp:96
static map_location::direction s
unit_type_data unit_types
Definition types.cpp:1499
static lg::log_domain log_wml("wml")
static lg::log_domain log_config("config")
#define e
#define f
#define c