The Battle for Wesnoth  1.15.3+dev
side_filter.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2018 by Yurii Chernyi <terraninfo@terraninfo.net>
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 
43 
44 side_filter::side_filter(const vconfig& cfg, const filter_context * fc, bool flat_tod)
45  : cfg_(cfg)
46  , flat_(flat_tod)
47  , side_string_()
48  , fc_(fc)
49 {
50 }
51 
52 side_filter::side_filter(const std::string &side_string, const filter_context * fc, bool flat_tod)
53  : cfg_(vconfig::empty_vconfig()), flat_(flat_tod), side_string_(side_string), fc_(fc)
54 {
55 }
56 
57 std::vector<int> side_filter::get_teams() const
58 {
59  assert(fc_);
60  //@todo: replace with better implementation
61  std::vector<int> result;
62  for(const team &t : fc_->get_disp_context().teams()) {
63  if (match(t)) {
64  result.push_back(t.side());
65  }
66  }
67  return result;
68 }
69 
70 static bool check_side_number(const team &t, const std::string &str)
71 {
72  std::vector<std::pair<int,int>> ranges = utils::parse_ranges(str);
73  int side_number = t.side();
74 
75  std::vector<std::pair<int,int>>::const_iterator range, range_end = ranges.end();
76  for (range = ranges.begin(); range != range_end; ++range) {
77  if(side_number >= range->first && side_number <= range->second) {
78  return true;
79  }
80  }
81  return false;
82 }
83 
85 {
86  assert(fc_);
87 
88  if (cfg_.has_attribute("side_in")) {
89  if (!check_side_number(t,cfg_["side_in"])) {
90  return false;
91  }
92  }
93  if (cfg_.has_attribute("side")) {
94  if (!check_side_number(t,cfg_["side"])) {
95  return false;
96  }
97  }
98  if (!side_string_.empty()) {
100  return false;
101  }
102  }
103 
104  config::attribute_value cfg_team_name = cfg_["team_name"];
105  if (!cfg_team_name.blank()) {
106  const std::string& that_team_name = cfg_team_name;
107  const std::string& this_team_name = t.team_name();
108 
109  if(std::find(this_team_name.begin(), this_team_name.end(), ',') == this_team_name.end()) {
110  if(this_team_name != that_team_name) return false;
111  }
112  else {
113  const std::vector<std::string>& these_team_names = utils::split(this_team_name);
114  bool search_futile = true;
115  for(const std::string& this_single_team_name : these_team_names) {
116  if(this_single_team_name == that_team_name) {
117  search_futile = false;
118  break;
119  }
120  }
121  if(search_futile) return false;
122  }
123  }
124 
125  //Allow filtering on units
126  if(cfg_.has_child("has_unit")) {
127  const vconfig & ufilt_cfg = cfg_.child("has_unit");
128  if (!ufilter_) {
129  ufilter_.reset(new unit_filter(ufilt_cfg.make_safe()));
130  ufilter_->set_use_flat_tod(flat_);
131  }
132  bool found = false;
133  for(const unit &u : fc_->get_disp_context().units()) {
134  if (u.side() != t.side()) {
135  continue;
136  }
137  if (ufilter_->matches(u)) {
138  found = true;
139  break;
140  }
141  }
142  if(!found && ufilt_cfg["search_recall_list"].to_bool(false)) {
143  for(const unit_const_ptr u : t.recall_list()) {
144  scoped_recall_unit this_unit("this_unit", t.save_id_or_number(), t.recall_list().find_index(u->id()));
145  if(ufilter_->matches(*u)) {
146  found = true;
147  break;
148  }
149  }
150  }
151  if (!found) {
152  return false;
153  }
154  }
155 
156  const vconfig& enemy_of = cfg_.child("enemy_of");
157  if(!enemy_of.null()) {
158  if (!enemy_filter_)
159  enemy_filter_.reset(new side_filter(enemy_of, fc_));
160  const std::vector<int>& teams = enemy_filter_->get_teams();
161  if(teams.empty()) return false;
162  for(const int side : teams) {
163  if(!fc_->get_disp_context().get_team(side).is_enemy(t.side()))
164  return false;
165  }
166  }
167 
168  const vconfig& allied_with = cfg_.child("allied_with");
169  if(!allied_with.null()) {
170  if (!allied_filter_)
171  allied_filter_.reset(new side_filter(allied_with, fc_));
172  const std::vector<int>& teams = allied_filter_->get_teams();
173  if(teams.empty()) return false;
174  for(const int side : teams) {
175  if(fc_->get_disp_context().get_team(side).is_enemy(t.side()))
176  return false;
177  }
178  }
179 
180  const vconfig& has_enemy = cfg_.child("has_enemy");
181  if(!has_enemy.null()) {
182  if (!has_enemy_filter_)
183  has_enemy_filter_.reset(new side_filter(has_enemy, fc_));
184  const std::vector<int>& teams = has_enemy_filter_->get_teams();
185  bool found = false;
186  for(const int side : teams) {
187  if(fc_->get_disp_context().get_team(side).is_enemy(t.side()))
188  {
189  found = true;
190  break;
191  }
192  }
193  if (!found) return false;
194  }
195 
196  const vconfig& has_ally = cfg_.child("has_ally");
197  if(!has_ally.null()) {
198  if (!has_ally_filter_)
199  has_ally_filter_.reset(new side_filter(has_ally, fc_));
200  const std::vector<int>& teams = has_ally_filter_->get_teams();
201  bool found = false;
202  for(const int side : teams) {
203  if(!fc_->get_disp_context().get_team(side).is_enemy(t.side()))
204  {
205  found = true;
206  break;
207  }
208  }
209  if (!found) return false;
210  }
211 
212 
213  const config::attribute_value cfg_controller = cfg_["controller"];
214  if (!cfg_controller.blank())
215  {
216  if (resources::controller->is_networked_mp() && synced_context::is_synced()) {
217  ERR_NG << "ignoring controller= in SSF due to danger of OOS errors" << std::endl;
218  }
219  else {
220  bool found = false;
221  for(const std::string& controller : utils::split(cfg_controller))
222  {
223  if(t.controller().to_string() == controller) {
224  found = true;
225  }
226  }
227  if(!found) {
228  return false;
229  }
230  }
231  }
232 
233  if (cfg_.has_attribute("formula")) {
234  try {
235  const wfl::team_callable callable(t);
236  const wfl::formula form(cfg_["formula"], new wfl::gamestate_function_symbol_table);
237  if(!form.evaluate(callable).as_bool()) {
238  return false;
239  }
240  return true;
241  } catch(const wfl::formula_error& e) {
242  lg::wml_error() << "Formula error in side filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
243  // Formulae with syntax errors match nothing
244  return false;
245  }
246  }
247 
248  if (cfg_.has_attribute("lua_function")) {
249  std::string lua_function = cfg_["lua_function"].str();
250  if (!lua_function.empty() && fc_->get_lua_kernel()) {
251  if (!fc_->get_lua_kernel()->run_filter(lua_function.c_str(), t)) {
252  return false;
253  }
254  }
255  }
256 
257 
258  return true;
259 }
260 
261 bool side_filter::match(int side) const
262 {
263  assert(fc_);
264  return this->match((fc_->get_disp_context().get_team(side)));
265 }
266 
267 bool side_filter::match(const team& t) const
268 {
269  bool matches = match_internal(t);
270 
271  //handle [and], [or], and [not] with in-order precedence
274  while (cond != cond_end) {
275  const std::string& cond_name = cond.get_key();
276  const vconfig& cond_cfg = cond.get_child();
277 
278  //handle [and]
279  if(cond_name == "and")
280  {
281  matches = matches && side_filter(cond_cfg, fc_, flat_).match(t);
282  }
283  //handle [or]
284  else if(cond_name == "or")
285  {
286  matches = matches || side_filter(cond_cfg, fc_, flat_).match(t);
287  }
288  //handle [not]
289  else if(cond_name == "not")
290  {
291  matches = matches && !side_filter(cond_cfg, fc_, flat_).match(t);
292  }
293  ++cond;
294  }
295  return matches;
296 }
play_controller * controller
Definition: resources.cpp:21
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
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:60
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:129
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. It should be a pointer because otherwise the default ctor doesn&#39;t...
Definition: side_filter.hpp:54
all_children_iterator ordered_begin() const
In-order iteration over all children.
Definition: variable.cpp:477
bool match_internal(const team &t) const
Definition: side_filter.cpp:84
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:106
std::string save_id_or_number() const
Definition: team.hpp:232
std::string get_key() const
Definition: variable.cpp:444
-file sdl_utils.hpp
Definitions for the interface to Wesnoth Markup Language (WML).
std::vector< std::string > split(const std::string &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
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)
all_children_iterator ordered_end() const
Definition: variable.cpp:482
#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:243
virtual const unit_map & units() const =0
std::unique_ptr< unit_filter > ufilter_
Definition: side_filter.hpp:56
std::string side_string_
Definition: side_filter.hpp:52
virtual const std::vector< team > & teams() const =0
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:269
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:49
std::unique_ptr< side_filter > has_ally_filter_
Definition: side_filter.hpp:59
bool match(const team &t) const
double t
Definition: astarsearch.cpp:64
bool find(E event, F functor)
Tests whether an event handler is available.
static bool check_side_number(const team &t, const std::string &str)
Definition: side_filter.cpp:70
static int cond(LexState *ls)
Definition: lparser.cpp:1177
std::vector< int > get_teams() const
Definition: side_filter.cpp:57
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:57
virtual game_lua_kernel * get_lua_kernel() const =0
recall_list_manager & recall_list()
Definition: team.hpp:215
#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:314
int side() const
Definition: team.hpp:188
bool null() const
Definition: variable.hpp:80
static bool is_synced()
const std::string & team_name() const
Definition: team.hpp:297
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:52
std::unique_ptr< side_filter > enemy_filter_
Definition: side_filter.hpp:58
CONTROLLER controller() const
Definition: team.hpp:255