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