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