The Battle for Wesnoth  1.15.0-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 http://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 "filter_context.hpp"
19 #include "log.hpp"
20 #include "recall_list_manager.hpp"
21 #include "side_filter.hpp"
22 #include "variable.hpp"
23 #include "team.hpp"
25 #include "play_controller.hpp"
26 #include "resources.hpp"
27 #include "synced_context.hpp"
28 #include "units/unit.hpp"
29 #include "units/filter.hpp"
30 #include "units/map.hpp"
31 #include "variable.hpp"
32 #include "formula/callable_objects.hpp"
33 #include "formula/formula.hpp"
35 
36 static lg::log_domain log_engine_sf("engine/side_filter");
37 #define ERR_NG LOG_STREAM(err, log_engine_sf)
38 
40 
41 side_filter::side_filter(const vconfig& cfg, const filter_context * fc, bool flat_tod)
42  : cfg_(cfg)
43  , flat_(flat_tod)
44  , side_string_()
45  , fc_(fc)
46 {
47 }
48 
49 side_filter::side_filter(const std::string &side_string, const filter_context * fc, bool flat_tod)
50  : cfg_(vconfig::empty_vconfig()), flat_(flat_tod), side_string_(side_string), fc_(fc)
51 {
52 }
53 
54 std::vector<int> side_filter::get_teams() const
55 {
56  assert(fc_);
57  //@todo: replace with better implementation
58  std::vector<int> result;
59  for(const team &t : fc_->get_disp_context().teams()) {
60  if (match(t)) {
61  result.push_back(t.side());
62  }
63  }
64  return result;
65 }
66 
67 static bool check_side_number(const team &t, const std::string &str)
68 {
69  std::vector<std::pair<int,int>> ranges = utils::parse_ranges(str);
70  int side_number = t.side();
71 
72  std::vector<std::pair<int,int>>::const_iterator range, range_end = ranges.end();
73  for (range = ranges.begin(); range != range_end; ++range) {
74  if(side_number >= range->first && side_number <= range->second) {
75  return true;
76  }
77  }
78  return false;
79 }
80 
82 {
83  assert(fc_);
84 
85  if (cfg_.has_attribute("side_in")) {
86  if (!check_side_number(t,cfg_["side_in"])) {
87  return false;
88  }
89  }
90  if (cfg_.has_attribute("side")) {
91  if (!check_side_number(t,cfg_["side"])) {
92  return false;
93  }
94  }
95  if (!side_string_.empty()) {
97  return false;
98  }
99  }
100 
101  config::attribute_value cfg_team_name = cfg_["team_name"];
102  if (!cfg_team_name.blank()) {
103  const std::string& that_team_name = cfg_team_name;
104  const std::string& this_team_name = t.team_name();
105 
106  if(std::find(this_team_name.begin(), this_team_name.end(), ',') == this_team_name.end()) {
107  if(this_team_name != that_team_name) return false;
108  }
109  else {
110  const std::vector<std::string>& these_team_names = utils::split(this_team_name);
111  bool search_futile = true;
112  for(const std::string& this_single_team_name : these_team_names) {
113  if(this_single_team_name == that_team_name) {
114  search_futile = false;
115  break;
116  }
117  }
118  if(search_futile) return false;
119  }
120  }
121 
122  //Allow filtering on units
123  if(cfg_.has_child("has_unit")) {
124  const vconfig & ufilt_cfg = cfg_.child("has_unit");
125  if (!ufilter_) {
126  ufilter_.reset(new unit_filter(ufilt_cfg.make_safe()));
127  ufilter_->set_use_flat_tod(flat_);
128  }
129  bool found = false;
130  for(const unit &u : fc_->get_disp_context().units()) {
131  if (u.side() != t.side()) {
132  continue;
133  }
134  if (ufilter_->matches(u)) {
135  found = true;
136  break;
137  }
138  }
139  if(!found && ufilt_cfg["search_recall_list"].to_bool(false)) {
140  for(const unit_const_ptr & u : t.recall_list()) {
141  scoped_recall_unit this_unit("this_unit", t.save_id(),t.recall_list().find_index(u->id()));
142  if(ufilter_->matches(*u)) {
143  found = true;
144  break;
145  }
146  }
147  }
148  if (!found) {
149  return false;
150  }
151  }
152 
153  const vconfig& enemy_of = cfg_.child("enemy_of");
154  if(!enemy_of.null()) {
155  if (!enemy_filter_)
156  enemy_filter_.reset(new side_filter(enemy_of, fc_));
157  const std::vector<int>& teams = enemy_filter_->get_teams();
158  if(teams.empty()) return false;
159  for(const int side : teams) {
160  if(!fc_->get_disp_context().get_team(side).is_enemy(t.side()))
161  return false;
162  }
163  }
164 
165  const vconfig& allied_with = cfg_.child("allied_with");
166  if(!allied_with.null()) {
167  if (!allied_filter_)
168  allied_filter_.reset(new side_filter(allied_with, fc_));
169  const std::vector<int>& teams = allied_filter_->get_teams();
170  if(teams.empty()) return false;
171  for(const int side : teams) {
172  if(fc_->get_disp_context().get_team(side).is_enemy(t.side()))
173  return false;
174  }
175  }
176 
177  const vconfig& has_enemy = cfg_.child("has_enemy");
178  if(!has_enemy.null()) {
179  if (!has_enemy_filter_)
180  has_enemy_filter_.reset(new side_filter(has_enemy, fc_));
181  const std::vector<int>& teams = has_enemy_filter_->get_teams();
182  bool found = false;
183  for(const int side : teams) {
184  if(fc_->get_disp_context().get_team(side).is_enemy(t.side()))
185  {
186  found = true;
187  break;
188  }
189  }
190  if (!found) return false;
191  }
192 
193  const vconfig& has_ally = cfg_.child("has_ally");
194  if(!has_ally.null()) {
195  if (!has_ally_filter_)
196  has_ally_filter_.reset(new side_filter(has_ally, fc_));
197  const std::vector<int>& teams = has_ally_filter_->get_teams();
198  bool found = false;
199  for(const int side : teams) {
200  if(!fc_->get_disp_context().get_team(side).is_enemy(t.side()))
201  {
202  found = true;
203  break;
204  }
205  }
206  if (!found) return false;
207  }
208 
209 
210  const config::attribute_value cfg_controller = cfg_["controller"];
211  if (!cfg_controller.blank())
212  {
213  if (resources::controller->is_networked_mp() && synced_context::is_synced()) {
214  ERR_NG << "ignoring controller= in SSF due to danger of OOS errors" << std::endl;
215  }
216  else {
217  bool found = false;
218  for(const std::string& controller : utils::split(cfg_controller))
219  {
220  if(t.controller().to_string() == controller) {
221  found = true;
222  }
223  }
224  if(!found) {
225  return false;
226  }
227  }
228  }
229 
230  if (cfg_.has_attribute("formula")) {
231  try {
232  const wfl::team_callable callable(t);
233  const wfl::formula form(cfg_["formula"], new wfl::gamestate_function_symbol_table);
234  if(!form.evaluate(callable).as_bool()) {
235  return false;
236  }
237  return true;
238  } catch(const wfl::formula_error& e) {
239  lg::wml_error() << "Formula error in side filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
240  // Formulae with syntax errors match nothing
241  return false;
242  }
243  }
244 
245  return true;
246 }
247 
248 bool side_filter::match(int side) const
249 {
250  assert(fc_);
251  return this->match((fc_->get_disp_context().get_team(side)));
252 }
253 
254 bool side_filter::match(const team& t) const
255 {
256  bool matches = match_internal(t);
257 
258  //handle [and], [or], and [not] with in-order precedence
261  while (cond != cond_end) {
262  const std::string& cond_name = cond.get_key();
263  const vconfig& cond_cfg = cond.get_child();
264 
265  //handle [and]
266  if(cond_name == "and")
267  {
268  matches = matches && side_filter(cond_cfg, fc_, flat_).match(t);
269  }
270  //handle [or]
271  else if(cond_name == "or")
272  {
273  matches = matches || side_filter(cond_cfg, fc_, flat_).match(t);
274  }
275  //handle [not]
276  else if(cond_name == "not")
277  {
278  matches = matches && !side_filter(cond_cfg, fc_, flat_).match(t);
279  }
280  ++cond;
281  }
282  return matches;
283 }
play_controller * controller
Definition: resources.cpp:21
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
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:252
This class represents a single unit of a specific type.
Definition: unit.hpp:99
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:445
bool match_internal(const team &t) const
Definition: side_filter.cpp:81
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:99
const std::string & save_id() const
Definition: team.hpp:230
std::string get_key() const
Definition: variable.cpp:412
-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:127
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
all_children_iterator ordered_end() const
Definition: variable.cpp:450
#define ERR_NG
Definition: side_filter.cpp:37
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:241
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:279
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:63
static bool check_side_number(const team &t, const std::string &str)
Definition: side_filter.cpp:67
static int cond(LexState *ls)
Definition: lparser.cpp:1177
std::vector< int > get_teams() const
Definition: side_filter.cpp:54
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:57
recall_list_manager & recall_list()
Definition: team.hpp:214
#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:320
int side() const
Definition: team.hpp:187
bool null() const
Definition: variable.hpp:73
static bool is_synced()
const std::string & team_name() const
Definition: team.hpp:294
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:49
std::unique_ptr< side_filter > enemy_filter_
Definition: side_filter.hpp:58
CONTROLLER controller() const
Definition: team.hpp:253