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