The Battle for Wesnoth  1.17.23+dev
conditional_wml.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2023
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 "preferences/general.hpp"
28 #include "recall_list_manager.hpp"
29 #include "resources.hpp"
32 #include "team.hpp"
33 #include "terrain/filter.hpp"
34 #include "units/unit.hpp"
35 #include "units/filter.hpp"
36 #include "units/map.hpp"
37 #include "variable.hpp"
38 
39 static lg::log_domain log_engine("engine");
40 #define WRN_NG LOG_STREAM(warn, log_engine)
41 
42 static lg::log_domain log_wml("wml");
43 #define ERR_WML LOG_STREAM(err, log_wml)
44 
45 
46 // This file is in the game_events namespace.
47 namespace game_events {
48 
49 namespace builtin_conditions {
50  std::vector<std::pair<int,int>> default_counts = utils::parse_ranges_unsigned("1-infinity");
51 
52  bool have_unit(const vconfig& cfg)
53  {
55  return false;
56  }
57  std::vector<std::pair<int,int>> counts = cfg.has_attribute("count")
59  int match_count = 0;
60  const unit_filter ufilt(cfg);
61  for(const unit &i : resources::gameboard->units()) {
62  if(i.hitpoints() > 0 && ufilt(i)) {
63  ++match_count;
64  if(counts == default_counts) {
65  // by default a single match is enough, so avoid extra work
66  break;
67  }
68  }
69  }
70  if(cfg["search_recall_list"].to_bool()) {
71  for(const team& team : resources::gameboard->teams()) {
72  if(counts == default_counts && match_count) {
73  break;
74  }
75  for(std::size_t t = 0; t < team.recall_list().size(); ++t) {
76  if(counts == default_counts && match_count) {
77  break;
78  }
79  scoped_recall_unit auto_store("this_unit", team.save_id_or_number(), t);
80  if(ufilt(*team.recall_list()[t])) {
81  ++match_count;
82  }
83  }
84  }
85  }
86  return in_ranges(match_count, counts);
87  }
88 
89  bool have_location(const vconfig& cfg)
90  {
91  std::set<map_location> res;
92  terrain_filter(cfg, resources::filter_con, false).get_locations(res);
93 
94  std::vector<std::pair<int,int>> counts = cfg.has_attribute("count")
96  return in_ranges<int>(res.size(), counts);
97  }
98 
99  bool variable_matches(const vconfig& values)
100  {
101  if(values["name"].blank()) {
102  lg::log_to_chat() << "[variable] with missing name=\n";
103  ERR_WML << "[variable] with missing name=";
104  return true;
105  }
106  const std::string name = values["name"];
108 
109  if(auto n = values.get_config().attribute_count(); n > 2) {
110  lg::log_to_chat() << "[variable] name='" << name << "' found with multiple comparison attributes\n";
111  ERR_WML << "[variable] name='" << name << "' found with multiple comparison attributes";
112  } else if(n < 2) {
113  lg::log_to_chat() << "[variable] name='" << name << "' found with no comparison attribute\n";
114  ERR_WML << "[variable] name='" << name << "' found with no comparison attribute";
115  }
116 
117 #define TEST_STR_ATTR(name, test) \
118  do { \
119  if (values.has_attribute(name)) { \
120  std::string attr_str = values[name].str(); \
121  std::string str_value = value.str(); \
122  return (test); \
123  } \
124  } while (0)
125 
126 #define TEST_NUM_ATTR(name, test) \
127  do { \
128  if (values.has_attribute(name)) { \
129  double attr_num = values[name].to_double(); \
130  double num_value = value.to_double(); \
131  return (test); \
132  } \
133  } while (0)
134 
135 #define TEST_BOL_ATTR(name, test) \
136  do { \
137  if (values.has_attribute(name)) { \
138  bool attr_bool = values[name].to_bool(); \
139  bool bool_value = value.to_bool(); \
140  return (test); \
141  } \
142  } while (0)
143 
144  TEST_STR_ATTR("equals", str_value == attr_str);
145  TEST_STR_ATTR("not_equals", str_value != attr_str);
146  TEST_NUM_ATTR("numerical_equals", num_value == attr_num);
147  TEST_NUM_ATTR("numerical_not_equals", num_value != attr_num);
148  TEST_NUM_ATTR("greater_than", num_value > attr_num);
149  TEST_NUM_ATTR("less_than", num_value < attr_num);
150  TEST_NUM_ATTR("greater_than_equal_to", num_value >= attr_num);
151  TEST_NUM_ATTR("less_than_equal_to", num_value <= attr_num);
152  TEST_BOL_ATTR("boolean_equals", bool_value == attr_bool);
153  TEST_BOL_ATTR("boolean_not_equals", bool_value != attr_bool);
154  TEST_STR_ATTR("contains", str_value.find(attr_str) != std::string::npos);
155 
156 #undef TEST_STR_ATTR
157 #undef TEST_NUM_ATTR
158 #undef TEST_BOL_ATTR
159 
160  return true;
161  }
162 }
163 
164 namespace { // Support functions
165  bool internal_conditional_passed(const vconfig& cond)
166  {
167  if(cond.has_child("true")) {
168  return true;
169  }
170  if(cond.has_child("false")) {
171  return false;
172  }
173 
174  static const std::set<std::string> skip
175  {"then", "else", "elseif", "not", "and", "or", "do"};
176 
177  for(const auto& [key, filter] : cond.all_ordered()) {
178  if(std::find(skip.begin(), skip.end(), key) == skip.end()) {
179  assert(resources::lua_kernel);
180  if(!resources::lua_kernel->run_wml_conditional(key, filter)) {
181  return false;
182  }
183  }
184  }
185 
186  return true;
187  }
188 
189 } // end anonymous namespace (support functions)
190 
191 
192 bool conditional_passed(const vconfig& cond)
193 {
194  bool matches = internal_conditional_passed(cond);
195 
196  // Handle [and], [or], and [not] with in-order precedence
197  for(const auto& [key, filter] : cond.all_ordered()) {
198  // Handle [and]
199  if(key == "and") {
200  matches = matches && conditional_passed(filter);
201  }
202  // Handle [or]
203  else if(key == "or") {
204  matches = matches || conditional_passed(filter);
205  }
206  // Handle [not]
207  else if(key == "not") {
208  matches = matches && !conditional_passed(filter);
209  }
210  }
211 
212  return matches;
213 }
214 
215 } // end namespace game_events
double t
Definition: astarsearch.cpp:65
Variant for storing WML attributes.
std::size_t attribute_count() const
Count the number of non-blank attributes.
Definition: config.cpp:316
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:71
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:76
std::string save_id_or_number() const
Definition: team.hpp:220
recall_list_manager & recall_list()
Definition: team.hpp:203
This class represents a single unit of a specific type.
Definition: unit.hpp:135
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
bool has_attribute(const std::string &key) const
< Synonym for operator[]
Definition: variable.hpp:99
const config & get_config() const
Definition: variable.hpp:75
boost::iterator_range< all_children_iterator > all_ordered() const
Definition: variable.hpp:190
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_engine("engine")
#define TEST_BOL_ATTR(name, test)
#define ERR_WML
#define TEST_NUM_ATTR(name, test)
#define TEST_STR_ATTR(name, test)
static lg::log_domain log_wml("wml")
Define conditionals for the game's events mechanism, a.k.a.
std::size_t i
Definition: function.cpp:968
Standard logging facilities (interface).
bool in_ranges(const Cmp c, const std::vector< std::pair< Cmp, Cmp >> &ranges)
Definition: math.hpp:87
std::vector< std::pair< int, int > > default_counts
bool variable_matches(const vconfig &values)
bool have_location(const vconfig &cfg)
bool have_unit(const vconfig &cfg)
Domain specific events.
bool conditional_passed(const vconfig &cond)
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:468
game_board * gameboard
Definition: resources.cpp:21
game_data * gamedata
Definition: resources.cpp:23
game_lua_kernel * lua_kernel
Definition: resources.cpp:26
filter_context * filter_con
Definition: resources.cpp:24
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
std::vector< std::pair< int, int > > parse_ranges_unsigned(const std::string &str)
Handles a comma-separated list of inputs to parse_range, in a context that does not expect negative v...
static map_location::DIRECTION n