The Battle for Wesnoth  1.17.0-dev
conditional_wml.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2021
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Implementations of conditional action WML tags.
19  */
20 
22 
23 #include "config.hpp"
24 #include "game_board.hpp"
25 #include "game_data.hpp"
26 #include "log.hpp"
27 #include "recall_list_manager.hpp"
28 #include "resources.hpp"
31 #include "team.hpp"
32 #include "terrain/filter.hpp"
33 #include "units/unit.hpp"
34 #include "units/filter.hpp"
35 #include "units/map.hpp"
36 #include "variable.hpp"
37 
38 static lg::log_domain log_engine("engine");
39 #define WRN_NG LOG_STREAM(warn, log_engine)
40 
41 static lg::log_domain log_wml("wml");
42 #define ERR_WML LOG_STREAM(err, log_wml)
43 
44 
45 // This file is in the game_events namespace.
46 namespace game_events {
47 
48 namespace builtin_conditions {
49  std::vector<std::pair<int,int>> default_counts = utils::parse_ranges("1-infinity");
50 
51  bool have_unit(const vconfig& cfg)
52  {
54  return false;
55  }
56  std::vector<std::pair<int,int>> counts = cfg.has_attribute("count")
57  ? utils::parse_ranges(cfg["count"]) : default_counts;
58  int match_count = 0;
59  const unit_filter ufilt(cfg);
60  for(const unit &i : resources::gameboard->units()) {
61  if(i.hitpoints() > 0 && ufilt(i)) {
62  ++match_count;
63  if(counts == default_counts) {
64  // by default a single match is enough, so avoid extra work
65  break;
66  }
67  }
68  }
69  if(cfg["search_recall_list"].to_bool()) {
70  for(const team& team : resources::gameboard->teams()) {
71  if(counts == default_counts && match_count) {
72  break;
73  }
74  for(std::size_t t = 0; t < team.recall_list().size(); ++t) {
75  if(counts == default_counts && match_count) {
76  break;
77  }
78  scoped_recall_unit auto_store("this_unit", team.save_id_or_number(), t);
79  if(ufilt(*team.recall_list()[t])) {
80  ++match_count;
81  }
82  }
83  }
84  }
85  return in_ranges(match_count, counts);
86  }
87 
88  bool have_location(const vconfig& cfg)
89  {
90  std::set<map_location> res;
91  terrain_filter(cfg, resources::filter_con, false).get_locations(res);
92 
93  std::vector<std::pair<int,int>> counts = cfg.has_attribute("count")
94  ? utils::parse_ranges(cfg["count"]) : default_counts;
95  return in_ranges<int>(res.size(), counts);
96  }
97 
98  bool variable_matches(const vconfig& values)
99  {
100  if(values["name"].blank()) {
101  lg::log_to_chat() << "[variable] with missing name=\n";
102  ERR_WML << "[variable] with missing name=";
103  return true;
104  }
105  const std::string name = values["name"];
107 
108  if(auto n = values.get_config().attribute_count(); n > 2) {
109  lg::log_to_chat() << "[variable] name='" << name << "' found with multiple comparison attributes\n";
110  ERR_WML << "[variable] name='" << name << "' found with multiple comparison attributes";
111  } else if(n < 2) {
112  lg::log_to_chat() << "[variable] name='" << name << "' found with no comparison attribute\n";
113  ERR_WML << "[variable] name='" << name << "' found with no comparison attribute";
114  }
115 
116 #define TEST_STR_ATTR(name, test) \
117  do { \
118  if (values.has_attribute(name)) { \
119  std::string attr_str = values[name].str(); \
120  std::string str_value = value.str(); \
121  return (test); \
122  } \
123  } while (0)
124 
125 #define TEST_NUM_ATTR(name, test) \
126  do { \
127  if (values.has_attribute(name)) { \
128  double attr_num = values[name].to_double(); \
129  double num_value = value.to_double(); \
130  return (test); \
131  } \
132  } while (0)
133 
134 #define TEST_BOL_ATTR(name, test) \
135  do { \
136  if (values.has_attribute(name)) { \
137  bool attr_bool = values[name].to_bool(); \
138  bool bool_value = value.to_bool(); \
139  return (test); \
140  } \
141  } while (0)
142 
143  TEST_STR_ATTR("equals", str_value == attr_str);
144  TEST_STR_ATTR("not_equals", str_value != attr_str);
145  TEST_NUM_ATTR("numerical_equals", num_value == attr_num);
146  TEST_NUM_ATTR("numerical_not_equals", num_value != attr_num);
147  TEST_NUM_ATTR("greater_than", num_value > attr_num);
148  TEST_NUM_ATTR("less_than", num_value < attr_num);
149  TEST_NUM_ATTR("greater_than_equal_to", num_value >= attr_num);
150  TEST_NUM_ATTR("less_than_equal_to", num_value <= attr_num);
151  TEST_BOL_ATTR("boolean_equals", bool_value == attr_bool);
152  TEST_BOL_ATTR("boolean_not_equals", bool_value != attr_bool);
153  TEST_STR_ATTR("contains", str_value.find(attr_str) != std::string::npos);
154 
155 #undef TEST_STR_ATTR
156 #undef TEST_NUM_ATTR
157 #undef TEST_BOL_ATTR
158 
159  return true;
160  }
161 }
162 
163 namespace { // Support functions
164  bool internal_conditional_passed(const vconfig& cond)
165  {
166  if(cond.has_child("true")) {
167  return true;
168  }
169  if(cond.has_child("false")) {
170  return false;
171  }
172 
173  static const std::set<std::string> skip
174  {"then", "else", "elseif", "not", "and", "or", "do"};
175 
176  for(const auto& [key, filter] : cond.all_ordered()) {
177  if(std::find(skip.begin(), skip.end(), key) == skip.end()) {
178  assert(resources::lua_kernel);
179  if(!resources::lua_kernel->run_wml_conditional(key, filter)) {
180  return false;
181  }
182  }
183  }
184 
185  return true;
186  }
187 
188 } // end anonymous namespace (support functions)
189 
190 
192 {
193  bool matches = internal_conditional_passed(cond);
194 
195  // Handle [and], [or], and [not] with in-order precedence
196  for(const auto& [key, filter] : cond.all_ordered()) {
197  // Handle [and]
198  if(key == "and") {
199  matches = matches && conditional_passed(filter);
200  }
201  // Handle [or]
202  else if(key == "or") {
203  matches = matches || conditional_passed(filter);
204  }
205  // Handle [not]
206  else if(key == "not") {
207  matches = matches && !conditional_passed(filter);
208  }
209  }
210 
211  return matches;
212 }
213 
214 bool matches_special_filter(const config &cfg, const vconfig& filter)
215 {
216  if (!cfg) {
217  WRN_NG << "attempt to filter attack for an event with no attack data." << std::endl;
218  // better to not execute the event (so the problem is more obvious)
219  return false;
220  }
221  // Though it may seem wasteful to put this on the heap, it's necessary.
222  // matches_filter() could potentially call a WFL formula, which would call shared_from_this().
223  auto attack = std::make_shared<const attack_type>(cfg);
224  return attack->matches_filter(filter.get_parsed_config());
225 }
226 
227 } // end namespace game_events
#define TEST_STR_ATTR(name, test)
This class represents a single unit of a specific type.
Definition: unit.hpp:121
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:87
std::string save_id_or_number() const
Definition: team.hpp:244
#define TEST_BOL_ATTR(name, test)
Definitions for the interface to Wesnoth Markup Language (WML).
game_data * gamedata
Definition: resources.cpp:23
bool have_unit(const vconfig &cfg)
unsigned attribute_count() const
Count the number of non-blank attributes.
Definition: config.cpp:389
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
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:24
game_board * gameboard
Definition: resources.cpp:21
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:87
Define conditionals for the game&#39;s events mechanism, a.k.a.
std::size_t i
Definition: function.cpp:967
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
static lg::log_domain log_wml("wml")
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:68
double t
Definition: astarsearch.cpp:65
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:227
game_lua_kernel * lua_kernel
Definition: resources.cpp:26
#define WRN_NG
#define ERR_WML
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
static map_location::DIRECTION n
#define TEST_NUM_ATTR(name, test)
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:289
boost::iterator_range< all_children_iterator > all_ordered() const
Definition: variable.hpp:190