The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
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
24#include "game_events/pump.hpp"
25#include "game_events/manager_impl.hpp" // for standardize_name
26
28#include "formula/formula.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
42static 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
47static 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.
51namespace game_events
52{
53/* ** event_handler ** */
54
55event_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
69std::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
131void 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 }
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 }
202private:
204};
205
206struct 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 }
220private:
222};
223
224struct 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 }
240private:
242 bool first_;
243};
244
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 }
281private:
283 bool first_;
284};
285
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 }
308private:
310};
311
312static 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 */
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 }
355private:
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
374void event_handler::add_filter(std::unique_ptr<event_filter>&& filter)
375{
376 filters_.push_back(std::move(filter));
377}
378
383
384void 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
#define WRN_NG
#define DBG_NG
#define LOG_NG
std::vector< std::string > names
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
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
static specials_context_t make(specials_combatant &&self, specials_combatant &&other, bool attacking)
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
config get_parsed_config() const
Definition variable.cpp:177
const config & get_config() const
Definition variable.hpp:75
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:357
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
static lg::log_domain log_engine("engine")
static lg::log_domain log_event_handler("event_handler")
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
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:792
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