The Battle for Wesnoth  1.19.7+dev
conditional_wml.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
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_unsigned("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")
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")
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 
191 bool conditional_passed(const vconfig& cond)
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 } // end namespace game_events
double t
Definition: astarsearch.cpp:63
Variant for storing WML attributes.
std::size_t attribute_count() const
Count the number of non-blank attributes.
Definition: config.cpp:311
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:75
std::string save_id_or_number() const
Definition: team.hpp:218
recall_list_manager & recall_list()
Definition: team.hpp:201
This class represents a single unit of a specific type.
Definition: unit.hpp:133
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.
Definitions for the interface to Wesnoth Markup Language (WML).
std::size_t i
Definition: function.cpp:1029
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:520
game_board * gameboard
Definition: resources.cpp:20
game_data * gamedata
Definition: resources.cpp:22
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
filter_context * filter_con
Definition: resources.cpp:23
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
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...
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:140
static map_location::direction n