The Battle for Wesnoth  1.15.13+dev
conditional_wml.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.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 /**
16  * @file
17  * Implementations of conditional action WML tags.
18  */
19 
21 
22 #include "config.hpp"
23 #include "game_board.hpp"
24 #include "game_data.hpp"
25 #include "log.hpp"
26 #include "recall_list_manager.hpp"
27 #include "resources.hpp"
30 #include "team.hpp"
31 #include "terrain/filter.hpp"
32 #include "units/unit.hpp"
33 #include "units/filter.hpp"
34 #include "units/map.hpp"
35 #include "variable.hpp"
36 
37 static lg::log_domain log_engine("engine");
38 #define WRN_NG LOG_STREAM(warn, log_engine)
39 
40 
41 // This file is in the game_events namespace.
42 namespace game_events {
43 
44 namespace builtin_conditions {
45  std::vector<std::pair<int,int>> default_counts = utils::parse_ranges("1-infinity");
46 
47  bool have_unit(const vconfig& cfg)
48  {
50  return false;
51  }
52  std::vector<std::pair<int,int>> counts = cfg.has_attribute("count")
53  ? utils::parse_ranges(cfg["count"]) : default_counts;
54  int match_count = 0;
55  const unit_filter ufilt(cfg);
56  for(const unit &i : resources::gameboard->units()) {
57  if(i.hitpoints() > 0 && ufilt(i)) {
58  ++match_count;
59  if(counts == default_counts) {
60  // by default a single match is enough, so avoid extra work
61  break;
62  }
63  }
64  }
65  if(cfg["search_recall_list"].to_bool()) {
66  for(const team& team : resources::gameboard->teams()) {
67  if(counts == default_counts && match_count) {
68  break;
69  }
70  for(std::size_t t = 0; t < team.recall_list().size(); ++t) {
71  if(counts == default_counts && match_count) {
72  break;
73  }
74  scoped_recall_unit auto_store("this_unit", team.save_id_or_number(), t);
75  if(ufilt(*team.recall_list()[t])) {
76  ++match_count;
77  }
78  }
79  }
80  }
81  return in_ranges(match_count, counts);
82  }
83 
84  bool have_location(const vconfig& cfg)
85  {
86  std::set<map_location> res;
87  terrain_filter(cfg, resources::filter_con, false).get_locations(res);
88 
89  std::vector<std::pair<int,int>> counts = cfg.has_attribute("count")
90  ? utils::parse_ranges(cfg["count"]) : default_counts;
91  return in_ranges<int>(res.size(), counts);
92  }
93 
94  bool variable_matches(const vconfig& values)
95  {
96  if(values["name"].blank()) {
97  lg::wml_error() << "[variable] with missing name=\n";
98  return true;
99  }
100  const std::string name = values["name"];
102 
103  if(auto n = values.get_config().attribute_count(); n > 2) {
104  lg::wml_error() << "[variable] name='" << name << "' found with multiple comparison attributes\n";
105  } else if(n < 2) {
106  lg::wml_error() << "[variable] name='" << name << "' found with no comparison attribute\n";
107  }
108 
109 #define TEST_STR_ATTR(name, test) \
110  do { \
111  if (values.has_attribute(name)) { \
112  std::string attr_str = values[name].str(); \
113  std::string str_value = value.str(); \
114  return (test); \
115  } \
116  } while (0)
117 
118 #define TEST_NUM_ATTR(name, test) \
119  do { \
120  if (values.has_attribute(name)) { \
121  double attr_num = values[name].to_double(); \
122  double num_value = value.to_double(); \
123  return (test); \
124  } \
125  } while (0)
126 
127 #define TEST_BOL_ATTR(name, test) \
128  do { \
129  if (values.has_attribute(name)) { \
130  bool attr_bool = values[name].to_bool(); \
131  bool bool_value = value.to_bool(); \
132  return (test); \
133  } \
134  } while (0)
135 
136  TEST_STR_ATTR("equals", str_value == attr_str);
137  TEST_STR_ATTR("not_equals", str_value != attr_str);
138  TEST_NUM_ATTR("numerical_equals", num_value == attr_num);
139  TEST_NUM_ATTR("numerical_not_equals", num_value != attr_num);
140  TEST_NUM_ATTR("greater_than", num_value > attr_num);
141  TEST_NUM_ATTR("less_than", num_value < attr_num);
142  TEST_NUM_ATTR("greater_than_equal_to", num_value >= attr_num);
143  TEST_NUM_ATTR("less_than_equal_to", num_value <= attr_num);
144  TEST_BOL_ATTR("boolean_equals", bool_value == attr_bool);
145  TEST_BOL_ATTR("boolean_not_equals", bool_value != attr_bool);
146  TEST_STR_ATTR("contains", str_value.find(attr_str) != std::string::npos);
147 
148 #undef TEST_STR_ATTR
149 #undef TEST_NUM_ATTR
150 #undef TEST_BOL_ATTR
151 
152  return true;
153  }
154 }
155 
156 namespace { // Support functions
157  bool internal_conditional_passed(const vconfig& cond)
158  {
159  if(cond.has_child("true")) {
160  return true;
161  }
162  if(cond.has_child("false")) {
163  return false;
164  }
165 
166  static const std::set<std::string> skip
167  {"then", "else", "elseif", "not", "and", "or", "do"};
168 
169  for(const auto& [key, filter] : cond.all_ordered()) {
170  if(std::find(skip.begin(), skip.end(), key) == skip.end()) {
171  assert(resources::lua_kernel);
172  if(!resources::lua_kernel->run_wml_conditional(key, filter)) {
173  return false;
174  }
175  }
176  }
177 
178  return true;
179  }
180 
181 } // end anonymous namespace (support functions)
182 
183 
185 {
186  bool matches = internal_conditional_passed(cond);
187 
188  // Handle [and], [or], and [not] with in-order precedence
189  for(const auto& [key, filter] : cond.all_ordered()) {
190  // Handle [and]
191  if(key == "and") {
192  matches = matches && conditional_passed(filter);
193  }
194  // Handle [or]
195  else if(key == "or") {
196  matches = matches || conditional_passed(filter);
197  }
198  // Handle [not]
199  else if(key == "not") {
200  matches = matches && !conditional_passed(filter);
201  }
202  }
203 
204  return matches;
205 }
206 
207 bool matches_special_filter(const config &cfg, const vconfig& filter)
208 {
209  if (!cfg) {
210  WRN_NG << "attempt to filter attack for an event with no attack data." << std::endl;
211  // better to not execute the event (so the problem is more obvious)
212  return false;
213  }
214  // Though it may seem wasteful to put this on the heap, it's necessary.
215  // matches_filter() could potentially call a WFL formula, which would call shared_from_this().
216  auto attack = std::make_shared<const attack_type>(cfg);
217  return attack->matches_filter(filter.get_parsed_config());
218 }
219 
220 } // end namespace game_events
#define TEST_STR_ATTR(name, test)
This class represents a single unit of a specific type.
Definition: unit.hpp:120
Variant for storing WML attributes.
bool have_location(const vconfig &cfg)
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition: variable.hpp:99
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp >> &ranges)
Definition: math.hpp:86
std::string save_id_or_number() const
Definition: team.hpp:240
#define TEST_BOL_ATTR(name, test)
Definitions for the interface to Wesnoth Markup Language (WML).
game_data * gamedata
Definition: resources.cpp:22
bool have_unit(const vconfig &cfg)
unsigned attribute_count() const
Count the number of non-blank attributes.
Definition: config.cpp:401
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
std::vector< std::pair< int, int > > parse_ranges(const std::string &str)
std::vector< std::pair< int, int > > default_counts
filter_context * filter_con
Definition: resources.cpp:23
game_board * gameboard
Definition: resources.cpp:20
config get_parsed_config() const
Definition: variable.cpp:176
bool variable_matches(const vconfig &values)
static lg::log_domain log_engine("engine")
Domain specific events.
Definition: action_wml.cpp:86
Define conditionals for the game&#39;s events mechanism, a.k.a.
std::size_t i
Definition: function.cpp:940
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:288
bool matches_special_filter(const config &cfg, const vconfig &filter)
bool has_child(const std::string &key) const
Returns whether or not *this has a child whose key is key.
Definition: variable.cpp:315
std::size_t size() const
Get the number of units on the list.
bool conditional_passed(const vconfig &cond)
virtual config::attribute_value get_variable_const(const std::string &varname) const
returns a blank attribute value if varname is no valid variable name.
Definition: game_data.cpp:66
double t
Definition: astarsearch.cpp:64
static int cond(LexState *ls)
Definition: lparser.cpp:1394
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
const config & get_config() const
Definition: variable.hpp:75
Standard logging facilities (interface).
recall_list_manager & recall_list()
Definition: team.hpp:223
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
#define WRN_NG
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
static map_location::DIRECTION n
#define TEST_NUM_ATTR(name, test)
boost::iterator_range< all_children_iterator > all_ordered() const
Definition: variable.hpp:190