The Battle for Wesnoth  1.19.5+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  , has_preloaded_(false)
61  , event_ref_(0)
62  , priority_(0.0)
63  , args_()
64  , filters_()
65  , id_(id)
66  , types_(types)
67 {}
68 
69 std::vector<std::string> event_handler::names(const variable_set* vars) const
70 {
71  std::string names = types_;
72 
73  // Do some standardization on the name field.
74  // Split the name field and standardize each one individually.
75  // This ensures they're all valid for by-name lookup.
76  std::vector<std::string> standardized_names;
77  for(std::string single_name : utils::split(names)) {
78  if(utils::might_contain_variables(single_name)) {
79  if(!vars) {
80  // If we don't have gamedata, we can't interpolate variables, so there's
81  // no way the name will match. Move on to the next one in that case.
82  continue;
83  }
84  single_name = utils::interpolate_variables_into_string(single_name, *vars);
85  }
86  // Variable interpolation could've introduced additional commas, so split again.
87  for(const std::string& subname : utils::split(single_name)) {
88  standardized_names.emplace_back(event_handlers::standardize_name(subname));
89  }
90  }
91 
92  return standardized_names;
93 }
94 
96 {
97  return args_.empty();
98 }
99 
101 {
102  assert(!disabled_ && "Trying to disable a disabled event. Shouldn't happen!");
103  disabled_ = true;
104 }
105 
107 {
108  if(disabled_) {
109  return;
110  }
111 
112  if(is_menu_item_) {
113  DBG_NG << "menu item " << id_ << " will now invoke the following command(s):\n" << args_;
114  }
115 
116  if(first_time_only_) {
117  disable();
118  }
119 
120  lk.run_wml_event(event_ref_, vconfig(args_, false), event_info);
122 }
123 
125 {
126  return std::all_of(filters_.begin(), filters_.end(), [&ev](const auto& filter) {
127  return (*filter)(ev);
128  });
129 }
130 
131 void event_handler::write_config(config &cfg, bool include_nonserializable) const
132 {
133  if(disabled_) {
134  WRN_NG << "Tried to serialize disabled event, skipping";
135  return;
136  }
137  static const char* log_append_preload = " - this will not break saves since it was registered during or before preload\n";
138  static const char* log_append_postload = " - this will break saves because it was registered after preload\n";
139  if(is_lua_) {
140  if(include_nonserializable) {
141  cfg["nonserializable"] = true;
142  cfg.add_child("lua")["code"] = "<function>";
143  } else {
144  static const char* log = "Skipping serialization of an event with action bound to Lua code";
145  if(has_preloaded_){
146  WRN_NG << log << log_append_postload;
147  lg::log_to_chat() << log << log_append_postload;
148  } else {
149  LOG_NG << log << log_append_preload;
150  }
151  return;
152  }
153  }
154  if(!std::all_of(filters_.begin(), filters_.end(), std::mem_fn(&event_filter::can_serialize))) {
155  if(include_nonserializable) {
156  cfg["nonserializable"] = true;
157  } else {
158  static const char* log = "Skipping serialization of an event with filter bound to Lua code";
159  if(has_preloaded_) {
160  WRN_NG << log << log_append_postload;
161  lg::log_to_chat() << log << log_append_postload;
162  } else {
163  LOG_NG << log << log_append_preload;
164  }
165  return;
166  }
167  }
168  if(!types_.empty()) cfg["name"] = types_;
169  if(!id_.empty()) cfg["id"] = id_;
170  cfg["first_time_only"] = first_time_only_;
171  cfg["priority"] = priority_;
172  for(const auto& filter : filters_) {
173  filter->serialize(cfg);
174  }
175  cfg.append(args_);
176 }
177 
179 {
180  WRN_NG << "Tried to serialize an event with a filter that cannot be serialized!";
181 }
182 
184 {
185  return false;
186 }
187 
189  filter_condition(const vconfig& cfg) : cfg_(cfg.make_safe()) {}
190  bool operator()(const queued_event&) const override
191  {
192  return conditional_passed(cfg_);
193  }
194  void serialize(config& cfg) const override
195  {
196  cfg.add_child("filter_condition", cfg_.get_config());
197  }
198  bool can_serialize() const override
199  {
200  return true;
201  }
202 private:
204 };
205 
206 struct filter_side : public event_filter {
207  filter_side(const vconfig& cfg) : ssf_(cfg.make_safe(), &resources::controller->gamestate()) {}
208  bool operator()(const queued_event&) const override
209  {
210  return ssf_.match(resources::controller->current_side());
211  }
212  void serialize(config& cfg) const override
213  {
214  cfg.add_child("filter_side", ssf_.get_config());
215  }
216  bool can_serialize() const override
217  {
218  return true;
219  }
220 private:
222 };
223 
224 struct filter_unit : public event_filter {
225  filter_unit(const vconfig& cfg, bool first) : suf_(cfg.make_safe()), first_(first) {}
226  bool operator()(const queued_event& event_info) const override
227  {
228  const auto& loc = first_ ? event_info.loc1 : event_info.loc2;
229  auto unit = resources::gameboard->units().find(loc);
230  return loc.matches_unit_filter(unit, suf_);
231  }
232  void serialize(config& cfg) const override
233  {
234  cfg.add_child(first_ ? "filter" : "filter_second", suf_.to_config());
235  }
236  bool can_serialize() const override
237  {
238  return true;
239  }
240 private:
242  bool first_;
243 };
244 
245 struct filter_attack : public event_filter {
246  filter_attack(const vconfig& cfg, bool first) : swf_(cfg.make_safe()), first_(first) {}
247  bool operator()(const queued_event& event_info) const override
248  {
249  const unit_map& units = resources::gameboard->units();
250  const auto& loc = first_ ? event_info.loc1 : event_info.loc2;
251  const auto& loc_d = first_ ? event_info.loc2 : event_info.loc1;
252  auto unit_a = units.find(loc);
253  auto unit_d = units.find(loc_d);
254  if(unit_a != units.end() && loc.matches_unit(unit_a)) {
255  const auto u = unit_a->shared_from_this();
256  auto temp_weapon = event_info.data.optional_child(first_ ? "first" : "second");
257  if(temp_weapon){
258  const_attack_ptr attack = std::make_shared<const attack_type>(*temp_weapon);
259  if(unit_d != units.end() && loc_d.matches_unit(unit_d)) {
260  const auto opp = unit_d->shared_from_this();
261  auto temp_other_weapon = event_info.data.optional_child(!first_ ? "first" : "second");
262  const_attack_ptr second_attack = temp_other_weapon ? std::make_shared<const attack_type>(*temp_other_weapon) : nullptr;
263  auto ctx = attack->specials_context(u, opp, loc, loc_d, first_, second_attack);
264  utils::optional<decltype(ctx)> opp_ctx;
265  if(second_attack){
266  opp_ctx.emplace(second_attack->specials_context(opp, u, loc_d, loc, !first_, attack));
267  }
268  return swf_.empty() || attack->matches_filter(swf_.get_parsed_config());
269  } else {
270  auto ctx = attack->specials_context(u, loc, first_);
271  return swf_.empty() || attack->matches_filter(swf_.get_parsed_config());
272  }
273  }
274  }
275  return false;
276  }
277  void serialize(config& cfg) const override
278  {
279  cfg.add_child(first_ ? "filter_attack" : "filter_second_attack", swf_.get_config());
280  }
281  bool can_serialize() const override
282  {
283  return true;
284  }
285 private:
287  bool first_;
288 };
289 
290 struct filter_formula : public event_filter {
291  filter_formula(const std::string& formula) : formula_(formula) {}
292  bool operator()(const queued_event& event_info) const override
293  {
295  wfl::event_callable evt(event_info);
297  return formula_.evaluate(data).as_bool();
298  }
299  void serialize(config& cfg) const override
300  {
301  std::string code = formula_.str();
302  if(cfg.has_attribute("filter_formula")) {
303  // This will probably never happen in practice, but handle it just in case it somehow can
304  code = "(" + cfg["filter_formula"].str() + ") and (" + code + ")";
305  }
306  cfg["filter_formula"] = code;
307  }
308  bool can_serialize() const override
309  {
310  return true;
311  }
312 private:
314 };
315 
316 static std::unique_ptr<event_filter> make_filter(const std::string& key, const vconfig& contents)
317 {
318  if(key == "filter_condition") {
319  return std::make_unique<filter_condition>(contents);
320  } else if(key == "filter_side") {
321  return std::make_unique<filter_side>(contents);
322  } else if(key == "filter") {
323  return std::make_unique<filter_unit>(contents, true);
324  } else if(key == "filter_attack") {
325  return std::make_unique<filter_attack>(contents, true);
326  } else if(key == "filter_second") {
327  return std::make_unique<filter_unit>(contents, false);
328  } else if(key == "filter_second_attack") {
329  return std::make_unique<filter_attack>(contents, false);
330  }
331  return nullptr;
332 }
333 
334 /**
335  * This is a dynamic wrapper for any filter type, specified via [insert_tag].
336  * It loads the filter contents from a variable and forwards it to the appropriate filter class.
337  */
338 struct filter_dynamic : public event_filter {
339  filter_dynamic(const std::string& tag, const std::string& var) : tag_(tag), var_(var) {}
340  bool operator()(const queued_event& event_info) const override
341  {
342  variable_access_const variable(var_, resources::gamedata->get_variables());
343  if(!variable.exists_as_container()) return false;
344  if(auto filter = make_filter(tag_, vconfig(variable.as_container()))) {
345  return (*filter)(event_info);
346  }
347  return false;
348  }
349  void serialize(config& cfg) const override
350  {
351  auto tag = cfg.add_child("insert_tag");
352  tag["name"] = tag_;
353  tag["variable"] = var_;
354  }
355  bool can_serialize() const override
356  {
357  return true;
358  }
359 private:
360  std::string tag_, var_;
361 };
362 
364 {
365  for(const auto [filter_key, filter_cfg] : cfg.all_children_view()) {
366  vconfig vcfg(filter_cfg);
367  if(auto filter_ptr = make_filter(filter_key, vcfg)) {
368  add_filter(std::move(filter_ptr));
369  } else if(filter_key == "insert_tag" && make_filter(vcfg["name"], vconfig::empty_vconfig())) {
370  add_filter(std::make_unique<filter_dynamic>(vcfg["name"], vcfg["variable"]));
371  }
372  }
373  if(cfg.has_attribute("filter_formula")) {
374  add_filter(std::make_unique<filter_formula>(cfg["filter_formula"]));
375  }
376 }
377 
378 void event_handler::add_filter(std::unique_ptr<event_filter>&& filter)
379 {
380  filters_.push_back(std::move(filter));
381 }
382 
384 {
385  event_ref_ = lk.save_wml_event();
386 }
387 
388 void event_handler::set_event_ref(int idx, bool has_preloaded)
389 {
390  event_ref_ = idx;
391  is_lua_ = true;
392  has_preloaded_ = has_preloaded;
393 }
394 
395 
396 } // end namespace game_events
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:203
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:810
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
bool empty() const
Definition: config.cpp:849
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:384
config & add_child(config_key_type key)
Definition: config.cpp:440
virtual const unit_map & units() const override
Definition: game_board.hpp:107
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:383
void add_filter(std::unique_ptr< event_filter > &&filter)
Definition: handlers.cpp:378
void read_filters(const config &cfg)
Definition: handlers.cpp:363
void write_config(config &cfg, bool include_nonserializable=false) const
Definition: handlers.cpp:131
std::vector< std::string > names(const variable_set *vars) const
Definition: handlers.cpp:69
void set_event_ref(int idx, bool has_preloaded)
Definition: handlers.cpp:388
void disable()
Flag this handler as disabled.
Definition: handlers.cpp:100
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:124
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:106
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:198
#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:316
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:543
std::unique_ptr< filter, std::function< void(filter *)> > filter_ptr
std::string tag(const std::string &tag_name, Args &&... contents)
Definition: markup.hpp:45
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:842
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:178
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:178
virtual bool can_serialize() const
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:183
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:247
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:277
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:281
filter_attack(const vconfig &cfg, bool first)
Definition: handlers.cpp:246
bool operator()(const queued_event &) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:190
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:194
filter_condition(const vconfig &cfg)
Definition: handlers.cpp:189
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:198
This is a dynamic wrapper for any filter type, specified via [insert_tag].
Definition: handlers.cpp:338
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:355
filter_dynamic(const std::string &tag, const std::string &var)
Definition: handlers.cpp:339
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:349
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:340
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:299
filter_formula(const std::string &formula)
Definition: handlers.cpp:291
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:308
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:292
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:216
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:212
bool operator()(const queued_event &) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:208
filter_side(const vconfig &cfg)
Definition: handlers.cpp:207
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:236
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:226
filter_unit(const vconfig &cfg, bool first)
Definition: handlers.cpp:225
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:232
entity_location loc1
Definition: pump.hpp:65
entity_location loc2
Definition: pump.hpp:66