The Battle for Wesnoth  1.17.10+dev
handlers.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2022
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  * The structure that tracks WML event handlers.
19  * (Typically, handlers are defined by [event] tags.)
20  */
21 
22 #include "game_events/handlers.hpp"
24 #include "game_events/pump.hpp"
25 #include "game_events/manager_impl.hpp" // for standardize_name
26 
27 #include "formula/callable_objects.hpp"
28 #include "formula/formula.hpp"
29 #include "formula/string_utils.hpp"
30 #include "game_board.hpp"
31 #include "game_data.hpp"
32 #include "log.hpp"
33 #include "play_controller.hpp"
34 #include "resources.hpp"
36 #include "side_filter.hpp"
37 #include "sound.hpp"
38 #include "units/filter.hpp"
39 #include "variable.hpp"
40 
41 static lg::log_domain log_engine("engine");
42 #define DBG_NG LOG_STREAM(debug, log_engine)
43 #define LOG_NG LOG_STREAM(info, log_engine)
44 #define WRN_NG LOG_STREAM(warn, log_engine)
45 
46 static lg::log_domain log_event_handler("event_handler");
47 #define DBG_EH LOG_STREAM(debug, log_event_handler)
48 
49 // This file is in the game_events namespace.
50 namespace game_events
51 {
52 /* ** event_handler ** */
53 
54 event_handler::event_handler(const std::string& types, const std::string& id)
55  : first_time_only_(true)
56  , is_menu_item_(false)
57  , disabled_(false)
58  , is_lua_(false)
59  , id_(id)
60  , types_(types)
61 {}
62 
63 std::vector<std::string> event_handler::names(const variable_set* vars) const
64 {
65  std::string names = types_;
66 
67  // Do some standardization on the name field.
68  // Split the name field and standardize each one individually.
69  // This ensures they're all valid for by-name lookup.
70  std::vector<std::string> standardized_names;
71  for(std::string single_name : utils::split(names)) {
72  if(utils::might_contain_variables(single_name)) {
73  if(!vars) {
74  // If we don't have gamedata, we can't interpolate variables, so there's
75  // no way the name will match. Move on to the next one in that case.
76  continue;
77  }
78  single_name = utils::interpolate_variables_into_string(single_name, *vars);
79  }
80  // Variable interpolation could've introduced additional commas, so split again.
81  for(const std::string& subname : utils::split(single_name)) {
82  standardized_names.emplace_back(event_handlers::standardize_name(subname));
83  }
84  }
85 
86  return standardized_names;
87 }
88 
90 {
91  return args_.empty();
92 }
93 
95 {
96  assert(!disabled_ && "Trying to disable a disabled event. Shouldn't happen!");
97  disabled_ = true;
98 }
99 
101 {
102  if(disabled_) {
103  return;
104  }
105 
106  if(is_menu_item_) {
107  DBG_NG << "menu item " << id_ << " will now invoke the following command(s):\n" << args_;
108  }
109 
110  if(first_time_only_) {
111  disable();
112  }
113 
114  lk.run_wml_event(event_ref_, vconfig(args_, false), event_info);
116 }
117 
119 {
120  return std::all_of(filters_.begin(), filters_.end(), [&ev](const auto& filter) {
121  return (*filter)(ev);
122  });
123 }
124 
125 void event_handler::write_config(config &cfg, bool include_nonserializable) const
126 {
127  if(disabled_) {
128  WRN_NG << "Tried to serialize disabled event, skipping";
129  return;
130  }
131  static const char* log_append_preload = " - this will not break saves since it was registered during or before preload\n";
132  static const char* log_append_postload = " - this will break saves because it was registered after preload\n";
133  if(is_lua_) {
134  if(include_nonserializable) {
135  cfg["nonserializable"] = true;
136  cfg.add_child("lua")["code"] = "<function>";
137  } else {
138  static const char* log = "Skipping serialization of an event with action bound to Lua code";
139  if(has_preloaded_){
140  WRN_NG << log << log_append_postload;
141  lg::log_to_chat() << log << log_append_postload;
142  } else {
143  LOG_NG << log << log_append_preload;
144  }
145  return;
146  }
147  }
148  if(!std::all_of(filters_.begin(), filters_.end(), std::mem_fn(&event_filter::can_serialize))) {
149  if(include_nonserializable) {
150  cfg["nonserializable"] = true;
151  } else {
152  static const char* log = "Skipping serialization of an event with filter bound to Lua code";
153  if(has_preloaded_) {
154  WRN_NG << log << log_append_postload;
155  lg::log_to_chat() << log << log_append_postload;
156  } else {
157  LOG_NG << log << log_append_preload;
158  }
159  return;
160  }
161  }
162  if(!types_.empty()) cfg["name"] = types_;
163  if(!id_.empty()) cfg["id"] = id_;
164  cfg["first_time_only"] = first_time_only_;
165  for(const auto& filter : filters_) {
166  filter->serialize(cfg);
167  }
168  cfg.append(args_);
169 }
170 
172 {
173  WRN_NG << "Tried to serialize an event with a filter that cannot be serialized!";
174 }
175 
177 {
178  return false;
179 }
180 
182  filter_condition(const vconfig& cfg) : cfg_(cfg.make_safe()) {}
183  bool operator()(const queued_event&) const override
184  {
185  return conditional_passed(cfg_);
186  }
187  void serialize(config& cfg) const override
188  {
189  cfg.add_child("filter_condition", cfg_.get_config());
190  }
191  bool can_serialize() const override
192  {
193  return true;
194  }
195 private:
197 };
198 
199 struct filter_side : public event_filter {
200  filter_side(const vconfig& cfg) : ssf_(cfg.make_safe(), &resources::controller->gamestate()) {}
201  bool operator()(const queued_event&) const override
202  {
203  return ssf_.match(resources::controller->current_side());
204  }
205  void serialize(config& cfg) const override
206  {
207  cfg.add_child("filter_side", ssf_.get_config());
208  }
209  bool can_serialize() const override
210  {
211  return true;
212  }
213 private:
215 };
216 
217 struct filter_unit : public event_filter {
218  filter_unit(const vconfig& cfg, bool first) : suf_(cfg.make_safe()), first_(first) {}
219  bool operator()(const queued_event& event_info) const override
220  {
221  const auto& loc = first_ ? event_info.loc1 : event_info.loc2;
222  auto unit = resources::gameboard->units().find(loc);
223  return loc.matches_unit_filter(unit, suf_);
224  }
225  void serialize(config& cfg) const override
226  {
227  cfg.add_child(first_ ? "filter" : "filter_second", suf_.to_config());
228  }
229  bool can_serialize() const override
230  {
231  return true;
232  }
233 private:
235  bool first_;
236 };
237 
238 struct filter_attack : public event_filter {
239  filter_attack(const vconfig& cfg, bool first) : swf_(cfg.make_safe()), first_(first) {}
240  bool operator()(const queued_event& event_info) const override
241  {
242  const unit_map& units = resources::gameboard->units();
243  const auto& loc = first_ ? event_info.loc1 : event_info.loc2;
244  auto unit = units.find(loc);
245  if(unit != units.end() && loc.matches_unit(unit)) {
246  const config& attack = event_info.data.child(first_ ? "first" : "second");
247  return swf_.empty() || matches_special_filter(attack, swf_);
248  }
249  return false;
250  }
251  void serialize(config& cfg) const override
252  {
253  cfg.add_child(first_ ? "filter_attack" : "filter_second_attack", swf_.get_config());
254  }
255  bool can_serialize() const override
256  {
257  return true;
258  }
259 private:
261  bool first_;
262 };
263 
264 struct filter_formula : public event_filter {
265  filter_formula(const std::string& formula) : formula_(formula) {}
266  bool operator()(const queued_event& event_info) const override
267  {
269  wfl::event_callable evt(event_info);
271  return formula_.evaluate(data).as_bool();
272  }
273  void serialize(config& cfg) const override
274  {
275  std::string code = formula_.str();
276  if(cfg.has_attribute("filter_formula")) {
277  // This will probably never happen in practice, but handle it just in case it somehow can
278  code = "(" + cfg["filter_formula"].str() + ") and (" + code + ")";
279  }
280  cfg["filter_formula"] = code;
281  }
282  bool can_serialize() const override
283  {
284  return true;
285  }
286 private:
288 };
289 
290 static std::unique_ptr<event_filter> make_filter(const std::string& key, const vconfig& contents)
291 {
292  if(key == "filter_condition") {
293  return std::make_unique<filter_condition>(contents);
294  } else if(key == "filter_side") {
295  return std::make_unique<filter_side>(contents);
296  } else if(key == "filter") {
297  return std::make_unique<filter_unit>(contents, true);
298  } else if(key == "filter_attack") {
299  return std::make_unique<filter_attack>(contents, true);
300  } else if(key == "filter_second") {
301  return std::make_unique<filter_unit>(contents, false);
302  } else if(key == "filter_second_attack") {
303  return std::make_unique<filter_attack>(contents, false);
304  }
305  return nullptr;
306 }
307 
308 /**
309  * This is a dynamic wrapper for any filter type, specified via [insert_tag].
310  * It loads the filter contents from a variable and forwards it to the appropriate filter class.
311  */
312 struct filter_dynamic : public event_filter {
313  filter_dynamic(const std::string& tag, const std::string& var) : tag_(tag), var_(var) {}
314  bool operator()(const queued_event& event_info) const override
315  {
316  variable_access_const variable(var_, resources::gamedata->get_variables());
317  if(!variable.exists_as_container()) return false;
318  if(auto filter = make_filter(tag_, vconfig(variable.as_container()))) {
319  return (*filter)(event_info);
320  }
321  return false;
322  }
323  void serialize(config& cfg) const override
324  {
325  auto tag = cfg.add_child("insert_tag");
326  tag["name"] = tag_;
327  tag["variable"] = var_;
328  }
329  bool can_serialize() const override
330  {
331  return true;
332  }
333 private:
334  std::string tag_, var_;
335 };
336 
338 {
339  for(auto filter : cfg.all_children_range()) {
340  vconfig vcfg(filter.cfg);
341  if(auto filter_ptr = make_filter(filter.key, vcfg)) {
342  add_filter(std::move(filter_ptr));
343  } else if(filter.key == "insert_tag" && make_filter(vcfg["name"], vconfig::empty_vconfig())) {
344  add_filter(std::make_unique<filter_dynamic>(vcfg["name"], vcfg["variable"]));
345  }
346  }
347  if(cfg.has_attribute("filter_formula")) {
348  add_filter(std::make_unique<filter_formula>(cfg["filter_formula"]));
349  }
350 }
351 
352 void event_handler::add_filter(std::unique_ptr<event_filter>&& filter)
353 {
354  filters_.push_back(std::move(filter));
355 }
356 
358 {
359  event_ref_ = lk.save_wml_event();
360 }
361 
362 void event_handler::set_event_ref(int idx, bool has_preloaded)
363 {
364  event_ref_ = idx;
365  is_lua_ = true;
366  has_preloaded_ = has_preloaded;
367 }
368 
369 
370 } // end namespace game_events
play_controller * controller
Definition: resources.cpp:22
static std::unique_ptr< event_filter > make_filter(const std::string &key, const vconfig &contents)
Definition: handlers.cpp:290
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:402
unit_iterator end()
Definition: map.hpp:429
entity_location loc2
Definition: pump.hpp:66
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:978
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
filter_condition(const vconfig &cfg)
Definition: handlers.cpp:182
virtual const unit_map & units() const override
Definition: game_board.hpp:113
This class represents a single unit of a specific type.
Definition: unit.hpp:133
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:269
static std::string standardize_name(const std::string &name)
Utility to standardize the event names used in by_name_.
bool is_lua_
Tracks whether the event was registered from the Lua API.
Definition: handlers.hpp:138
bool run_wml_event(int ref, const vconfig &args, const game_events::queued_event &ev, bool *out=nullptr)
Run a WML stored in the Lua registry.
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:266
bool has_attribute(config_key_type key) const
Definition: config.cpp:211
filter_attack(const vconfig &cfg, bool first)
Definition: handlers.cpp:239
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:282
event_handler(const std::string &types, const std::string &id="")
Definition: handlers.cpp:54
std::string_view data
Definition: picture.cpp:206
filter_formula(const std::string &formula)
Definition: handlers.cpp:265
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:219
maybe_const_t< config, V > & as_container() const
If instantiated with vi_policy_const, the lifetime of the returned const attribute_value reference mi...
virtual void serialize(config &cfg) const
Serializes the filter into a config, if possible.
Definition: handlers.cpp:171
void handle_event(const queued_event &event_info, game_lua_kernel &lk)
Handles the queued event, according to our WML instructions.
Definition: handlers.cpp:100
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:191
int save_wml_event()
Store a WML event in the Lua registry, as a function.
#define WRN_NG
Definition: handlers.cpp:44
game_data * gamedata
Definition: resources.cpp:23
bool filter_event(const queued_event &event_info) const
Definition: handlers.cpp:118
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:187
bool has_preloaded_
Tracks whether the event was registered before or after the Lua preload event fired.
Definition: handlers.hpp:150
virtual bool can_serialize() const
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:176
std::vector< std::shared_ptr< event_filter > > filters_
Definition: handlers.hpp:153
This is a dynamic wrapper for any filter type, specified via [insert_tag].
Definition: handlers.cpp:312
#define DBG_NG
Definition: handlers.cpp:42
void read_filters(const config &cfg)
Definition: handlers.cpp:337
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:323
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:229
void register_wml_event(game_lua_kernel &lk)
Definition: handlers.cpp:357
bool operator()(const queued_event &) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:183
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:273
#define LOG_NG
Definition: handlers.cpp:43
game_board * gameboard
Definition: resources.cpp:21
static lg::log_domain log_engine("engine")
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:314
void set_event_ref(int idx, bool has_preloaded)
Definition: handlers.cpp:362
filter_unit(const vconfig &cfg, bool first)
Definition: handlers.cpp:218
Domain specific events.
void disable()
Flag this handler as disabled.
Definition: handlers.cpp:94
filter_dynamic(const std::string &tag, const std::string &var)
Definition: handlers.cpp:313
unit_iterator find(std::size_t id)
Definition: map.cpp:301
Define conditionals for the game&#39;s events mechanism, a.k.a.
void write_config(config &cfg, bool include_nonserializable=false) const
Definition: handlers.cpp:125
An object representing the state of the game, providing access to the map and basic information...
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:251
bool matches_special_filter(const config &cfg, const vconfig &filter)
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:209
static lg::log_domain log_event_handler("event_handler")
Define the game&#39;s event mechanism.
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:240
bool operator()(const queued_event &) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:201
Represents a single filter condition on an event.
Definition: handlers.hpp:39
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:255
config & add_child(config_key_type key)
Definition: config.cpp:514
entity_location loc1
Definition: pump.hpp:65
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:225
bool might_contain_variables(const std::string &str)
Determines if a string might contain variables to interpolate.
bool conditional_passed(const vconfig &cond)
std::vector< std::string > names(const variable_set *vars) const
Definition: handlers.cpp:63
Define the handlers for the game&#39;s events mechanism.
Information on a WML variable.
An object representing the state of the current event; equivalent to Lua&#39;s wesnoth.current.event_context.
bool exists_as_container() const
std::vector< std::string > split(const config_attribute_value &val)
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Standard logging facilities (interface).
Container associating units to locations.
Definition: map.hpp:98
static vconfig empty_vconfig()
Definition: variable.cpp:141
void commit_music_changes()
Definition: sound.cpp:837
void add_filter(std::unique_ptr< event_filter > &&filter)
Definition: handlers.cpp:352
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
filter_side(const vconfig &cfg)
Definition: handlers.cpp:200
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:329
bool empty() const
Definition: config.cpp:941
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:412
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:205