The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
side_filter.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2010 - 2025
3 by Yurii Chernyi <terraninfo@terraninfo.net>
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#define GETTEXT_DOMAIN "wesnoth-lib"
17
18#include "config.hpp"
19#include "display_context.hpp"
20#include "filter_context.hpp"
21#include "log.hpp"
23#include "side_filter.hpp"
24#include "variable.hpp"
25#include "team.hpp"
28#include "play_controller.hpp"
29#include "resources.hpp"
30#include "synced_context.hpp"
31#include "units/unit.hpp"
32#include "units/filter.hpp"
33#include "units/map.hpp"
35#include "formula/formula.hpp"
37#include "utils/general.hpp"
38
39static lg::log_domain log_engine_sf("engine/side_filter");
40#define ERR_NG LOG_STREAM(err, log_engine_sf)
41
42static lg::log_domain log_wml("wml");
43#define ERR_WML LOG_STREAM(err, log_wml)
44
46
47side_filter::side_filter(const vconfig& cfg, const filter_context * fc, bool flat_tod)
48 : cfg_(cfg)
49 , flat_(flat_tod)
50 , side_string_()
51 , fc_(fc)
52{
53}
54
55side_filter::side_filter(const std::string &side_string, const filter_context * fc, bool flat_tod)
56 : cfg_(vconfig::empty_vconfig()), flat_(flat_tod), side_string_(side_string), fc_(fc)
57{
58}
59
60std::vector<int> side_filter::get_teams() const
61{
62 assert(fc_);
63 //@todo: replace with better implementation
64 std::vector<int> result;
65 for(const team &t : fc_->get_disp_context().teams()) {
66 if (match(t)) {
67 result.push_back(t.side());
68 }
69 }
70 return result;
71}
72
73static bool check_side_number(const team &t, const std::string &str)
74{
75 return in_ranges(t.side(), utils::parse_ranges_unsigned(str));
76}
77
79{
80 assert(fc_);
81
82 if (cfg_.has_attribute("side_in")) {
83 if (!check_side_number(t,cfg_["side_in"])) {
84 return false;
85 }
86 }
87 if (cfg_.has_attribute("side")) {
88 if (!check_side_number(t,cfg_["side"])) {
89 return false;
90 }
91 }
92 if (!side_string_.empty()) {
94 return false;
95 }
96 }
97
98 config::attribute_value cfg_team_name = cfg_["team_name"];
99 if (!cfg_team_name.blank()) {
100 const std::string& that_team_name = cfg_team_name;
101 const std::string& this_team_name = t.team_name();
102
103 if(!utils::contains(this_team_name, ',')) {
104 if(this_team_name != that_team_name) return false;
105 }
106 else {
107 const std::vector<std::string>& these_team_names = utils::split(this_team_name);
108 bool search_futile = true;
109 for(const std::string& this_single_team_name : these_team_names) {
110 if(this_single_team_name == that_team_name) {
111 search_futile = false;
112 break;
113 }
114 }
115 if(search_futile) return false;
116 }
117 }
118
119 //Allow filtering on units
120 if(cfg_.has_child("has_unit")) {
121 const vconfig & ufilt_cfg = cfg_.child("has_unit");
122 if (!ufilter_) {
123 ufilter_.reset(new unit_filter(ufilt_cfg.make_safe()));
124 ufilter_->set_use_flat_tod(flat_);
125 }
126 bool found = false;
127 for(const unit &u : fc_->get_disp_context().units()) {
128 if (u.side() != t.side()) {
129 continue;
130 }
131 if (ufilter_->matches(u)) {
132 found = true;
133 break;
134 }
135 }
136 if(!found && ufilt_cfg["search_recall_list"].to_bool(false)) {
137 for(const unit_const_ptr u : t.recall_list()) {
138 scoped_recall_unit this_unit("this_unit", t.save_id_or_number(), t.recall_list().find_index(u->id()));
139 if(ufilter_->matches(*u)) {
140 found = true;
141 break;
142 }
143 }
144 }
145 if (!found) {
146 return false;
147 }
148 }
149
150 const vconfig& enemy_of = cfg_.child("enemy_of");
151 if(!enemy_of.null()) {
152 if (!enemy_filter_)
153 enemy_filter_.reset(new side_filter(enemy_of, fc_));
154 const std::vector<int>& teams = enemy_filter_->get_teams();
155 if(teams.empty()) return false;
156 for(const int side : teams) {
157 if(!fc_->get_disp_context().get_team(side).is_enemy(t.side()))
158 return false;
159 }
160 }
161
162 const vconfig& allied_with = cfg_.child("allied_with");
163 if(!allied_with.null()) {
164 if (!allied_filter_)
165 allied_filter_.reset(new side_filter(allied_with, fc_));
166 const std::vector<int>& teams = allied_filter_->get_teams();
167 if(teams.empty()) return false;
168 for(const int side : teams) {
169 if(fc_->get_disp_context().get_team(side).is_enemy(t.side()))
170 return false;
171 }
172 }
173
174 const vconfig& has_enemy = cfg_.child("has_enemy");
175 if(!has_enemy.null()) {
177 has_enemy_filter_.reset(new side_filter(has_enemy, fc_));
178 const std::vector<int>& teams = has_enemy_filter_->get_teams();
179 bool found = false;
180 for(const int side : teams) {
181 if(fc_->get_disp_context().get_team(side).is_enemy(t.side()))
182 {
183 found = true;
184 break;
185 }
186 }
187 if (!found) return false;
188 }
189
190 const vconfig& has_ally = cfg_.child("has_ally");
191 if(!has_ally.null()) {
192 if (!has_ally_filter_)
193 has_ally_filter_.reset(new side_filter(has_ally, fc_));
194 const std::vector<int>& teams = has_ally_filter_->get_teams();
195 bool found = false;
196 for(const int side : teams) {
197 if(!fc_->get_disp_context().get_team(side).is_enemy(t.side()))
198 {
199 found = true;
200 break;
201 }
202 }
203 if (!found) return false;
204 }
205
206
207 const config::attribute_value cfg_controller = cfg_["controller"];
208 if (!cfg_controller.blank())
209 {
210 if (resources::controller->is_networked_mp() && synced_context::is_synced()) {
211 ERR_NG << "ignoring controller= in SSF due to danger of OOS errors";
212 }
213 else {
214 bool found = false;
215 for(const std::string& controller : utils::split(cfg_controller))
216 {
217 if(side_controller::get_string(t.controller()) == controller) {
218 found = true;
219 }
220 }
221 if(!found) {
222 return false;
223 }
224 }
225 }
226
227 if (cfg_.has_attribute("formula")) {
228 try {
229 const wfl::team_callable callable(t);
231 const wfl::formula form(cfg_["formula"], &symbols);
232 if(!form.evaluate(callable).as_bool()) {
233 return false;
234 }
235 return true;
236 } catch(const wfl::formula_error& e) {
237 lg::log_to_chat() << "Formula error in side filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
238 ERR_WML << "Formula error in side filter: " << e.type << " at " << e.filename << ':' << e.line << ")";
239 // Formulae with syntax errors match nothing
240 return false;
241 }
242 }
243
244 if (cfg_.has_attribute("lua_function")) {
245 std::string lua_function = cfg_["lua_function"].str();
246 if (!lua_function.empty() && fc_->get_lua_kernel()) {
247 if (!fc_->get_lua_kernel()->run_filter(lua_function.c_str(), t)) {
248 return false;
249 }
250 }
251 }
252
253
254 return true;
255}
256
257bool side_filter::match(int side) const
258{
259 assert(fc_);
260 return this->match((fc_->get_disp_context().get_team(side)));
261}
262
263bool side_filter::match(const team& t) const
264{
265 bool matches = match_internal(t);
266
267 // Handle [and], [or], and [not] with in-order precedence
268 for(const auto& [key, filter] : cfg_.all_ordered()) {
269 // Handle [and]
270 if(key == "and") {
271 matches = matches && side_filter(filter, fc_, flat_).match(t);
272 }
273 // Handle [or]
274 else if(key == "or") {
275 matches = matches || side_filter(filter, fc_, flat_).match(t);
276 }
277 // Handle [not]
278 else if(key == "not") {
279 matches = matches && !side_filter(filter, fc_, flat_).match(t);
280 }
281 }
282
283 return matches;
284}
#define ERR_NG
double t
Variant for storing WML attributes.
bool blank() const
Tests for an attribute that was never set.
const team & get_team(int side) const
This getter takes a 1-based side number, not a 0-based team number.
virtual const std::vector< team > & teams() const =0
virtual const unit_map & units() const =0
virtual game_lua_kernel * get_lua_kernel() const =0
virtual const display_context & get_disp_context() const =0
bool run_filter(char const *name, const unit &u)
Runs a script from a unit filter.
std::unique_ptr< side_filter > has_ally_filter_
std::vector< int > get_teams() const
const filter_context * fc_
The filter context for this filter.
std::string side_string_
bool match(const team &t) const
bool match_internal(const team &t) const
std::unique_ptr< side_filter > allied_filter_
std::unique_ptr< side_filter > has_enemy_filter_
const vconfig cfg_
std::unique_ptr< side_filter > enemy_filter_
side_filter(const std::string &side_string, const filter_context *fc, bool flat_tod=false)
std::unique_ptr< unit_filter > ufilter_
static bool is_synced()
This class stores all the data for a single 'side' (in game nomenclature).
Definition team.hpp:74
bool is_enemy(int n) const
Definition team.hpp:270
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
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition variable.hpp:99
bool null() const
Definition variable.hpp:72
vconfig child(const std::string &key) const
Returns a child of *this whose key is key.
Definition variable.cpp:289
const vconfig & make_safe() const
instruct the vconfig to make a private copy of its underlying data.
Definition variable.cpp:164
boost::iterator_range< all_children_iterator > all_ordered() const
Definition variable.hpp:190
bool has_child(const std::string &key) const
Returns whether or not *this has a child whose key is key.
Definition variable.cpp:316
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
bool as_bool() const
Returns a boolean state of the variant value.
Definition variant.cpp:357
Definitions for the interface to Wesnoth Markup Language (WML).
#define ERR_WML
const config * cfg
Standard logging facilities (interface).
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp > > &ranges)
Definition math.hpp:85
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition log.cpp:550
play_controller * controller
Definition resources.cpp:21
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
static lg::log_domain log_engine_sf("engine/side_filter")
static bool check_side_number(const team &t, const std::string &str)
static lg::log_domain log_wml("wml")
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition enum_base.hpp:46
#define e