The Battle for Wesnoth  1.19.19+dev
handlers.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2025
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 
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;
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 = specials_context_t::make({ u, loc, attack }, { opp, loc_d, second_attack }, first_);
264  return swf_.empty() || attack->matches_filter(swf_.get_parsed_config());
265  } else {
266  auto ctx = specials_context_t::make({ u, loc, attack }, { }, first_);
267  return swf_.empty() || attack->matches_filter(swf_.get_parsed_config());
268  }
269  }
270  }
271  return false;
272  }
273  void serialize(config& cfg) const override
274  {
275  cfg.add_child(first_ ? "filter_attack" : "filter_second_attack", swf_.get_config());
276  }
277  bool can_serialize() const override
278  {
279  return true;
280  }
281 private:
283  bool first_;
284 };
285 
286 struct filter_formula : public event_filter {
287  filter_formula(const std::string& formula) : formula_(formula) {}
288  bool operator()(const queued_event& event_info) const override
289  {
291  wfl::event_callable evt(event_info);
293  return formula_.evaluate(data).as_bool();
294  }
295  void serialize(config& cfg) const override
296  {
297  std::string code = formula_.str();
298  if(cfg.has_attribute("filter_formula")) {
299  // This will probably never happen in practice, but handle it just in case it somehow can
300  code = "(" + cfg["filter_formula"].str() + ") and (" + code + ")";
301  }
302  cfg["filter_formula"] = code;
303  }
304  bool can_serialize() const override
305  {
306  return true;
307  }
308 private:
310 };
311 
312 static std::unique_ptr<event_filter> make_filter(const std::string& key, const vconfig& contents)
313 {
314  if(key == "filter_condition") {
315  return std::make_unique<filter_condition>(contents);
316  } else if(key == "filter_side") {
317  return std::make_unique<filter_side>(contents);
318  } else if(key == "filter") {
319  return std::make_unique<filter_unit>(contents, true);
320  } else if(key == "filter_attack") {
321  return std::make_unique<filter_attack>(contents, true);
322  } else if(key == "filter_second") {
323  return std::make_unique<filter_unit>(contents, false);
324  } else if(key == "filter_second_attack") {
325  return std::make_unique<filter_attack>(contents, false);
326  }
327  return nullptr;
328 }
329 
330 /**
331  * This is a dynamic wrapper for any filter type, specified via [insert_tag].
332  * It loads the filter contents from a variable and forwards it to the appropriate filter class.
333  */
334 struct filter_dynamic : public event_filter {
335  filter_dynamic(const std::string& tag, const std::string& var) : tag_(tag), var_(var) {}
336  bool operator()(const queued_event& event_info) const override
337  {
338  variable_access_const variable(var_, resources::gamedata->get_variables());
339  if(!variable.exists_as_container()) return false;
340  if(auto filter = make_filter(tag_, vconfig(variable.as_container()))) {
341  return (*filter)(event_info);
342  }
343  return false;
344  }
345  void serialize(config& cfg) const override
346  {
347  auto tag = cfg.add_child("insert_tag");
348  tag["name"] = tag_;
349  tag["variable"] = var_;
350  }
351  bool can_serialize() const override
352  {
353  return true;
354  }
355 private:
356  std::string tag_, var_;
357 };
358 
360 {
361  for(const auto [filter_key, filter_cfg] : cfg.all_children_view()) {
362  vconfig vcfg(filter_cfg);
363  if(auto filter_ptr = make_filter(filter_key, vcfg)) {
364  add_filter(std::move(filter_ptr));
365  } else if(filter_key == "insert_tag" && make_filter(vcfg["name"], vconfig::empty_vconfig())) {
366  add_filter(std::make_unique<filter_dynamic>(vcfg["name"], vcfg["variable"]));
367  }
368  }
369  if(cfg.has_attribute("filter_formula")) {
370  add_filter(std::make_unique<filter_formula>(cfg["filter_formula"]));
371  }
372 }
373 
374 void event_handler::add_filter(std::unique_ptr<event_filter>&& filter)
375 {
376  filters_.push_back(std::move(filter));
377 }
378 
380 {
381  event_ref_ = lk.save_wml_event();
382 }
383 
384 void event_handler::set_event_ref(int idx, bool has_preloaded)
385 {
386  event_ref_ = idx;
387  is_lua_ = true;
388  has_preloaded_ = has_preloaded;
389 }
390 
391 
392 } // end namespace game_events
map_location loc
Definition: move.cpp:172
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
config & add_child(std::string_view key)
Definition: config.cpp:436
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:188
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:380
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:795
bool has_attribute(std::string_view key) const
Definition: config.cpp:157
bool empty() const
Definition: config.cpp:823
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:379
void add_filter(std::unique_ptr< event_filter > &&filter)
Definition: handlers.cpp:374
void read_filters(const config &cfg)
Definition: handlers.cpp:359
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:384
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
static specials_context_t make(specials_combatant &&self, specials_combatant &&other, bool attacking)
Definition: abilities.hpp:271
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:39
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:142
const config & get_config() const
Definition: variable.hpp:75
config get_parsed_config() const
Definition: variable.cpp:177
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:48
const std::string & str() const
Definition: formula.hpp:81
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.
const config * cfg
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:199
#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:312
std::stringstream & log_to_chat()
Use this to show WML errors in the ingame chat.
Definition: log.cpp:550
std::unique_ptr< filter, std::function< void(filter *)> > filter_ptr
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified tag.
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:817
constexpr auto filter
Definition: ranges.hpp:42
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:188
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:273
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:277
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:334
bool can_serialize() const override
Returns true if it is possible to serialize the filter into a config.
Definition: handlers.cpp:351
filter_dynamic(const std::string &tag, const std::string &var)
Definition: handlers.cpp:335
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:345
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:336
void serialize(config &cfg) const override
Serializes the filter into a config, if possible.
Definition: handlers.cpp:295
filter_formula(const std::string &formula)
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:304
bool operator()(const queued_event &event_info) const override
Runs the filter and returns whether it passes on the given event.
Definition: handlers.cpp:288
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