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