Enable setting an arbitrary Lua function as an event handler.
Unlike the old wesnoth.game_events.on_event hook and the "convenient" on_event() wrapper for it, this new functionality supports all of the features of WML events, with the sole exception of serialization, since it's not possible to reliably serialize a Lua function. This commit also divorces menu items from the event that they trigger. The undocumented wesnoth.interface.set_menu_item function no longer adds an event for the menu item; the caller needs to separately register an event using the new functionality.
This commit is contained in:
parent
11e5700848
commit
8cd1332630
18 changed files with 586 additions and 226 deletions
|
@ -64,7 +64,14 @@ if wesnoth.kernel_type() == "Game Lua Kernel" then
|
|||
wesnoth.print = wesnoth.deprecate_api('wesnoth.print', 'wesnoth.interface.add_overlay_text', 1, nil, function(cfg)
|
||||
wesnoth.wml_actions.print(cfg)
|
||||
end)
|
||||
-- No deprecation for these since since they're not actually public API yet
|
||||
wesnoth.set_menu_item = wesnoth.interface.set_menu_item
|
||||
wesnoth.set_menu_item = wesnoth.deprecate_api('wesnoth.set_menu_item', 'wesnoth.interface.set_menu_item', 1, nil, function(id, cfg)
|
||||
-- wesnoth.set_menu_item added both the menu item and the event that it triggers
|
||||
-- wesnoth.interface.set_menu_item only adds the menu item
|
||||
wesnoth.interface.set_menu_item(id, cfg)
|
||||
wesnoth.game_events.add(cfg.id, wesnoth.wml_actions.command, true)
|
||||
end)
|
||||
wesnoth.clear_menu_item = wesnoth.interface.clear_menu_item
|
||||
-- Event handlers don't have a separate module Lua file so dump those here
|
||||
wesnoth.add_event_handler = wesnoth.deprecate_api('wesnoth.add_event_hander', 'wesnoth.game_events.add', 1, nil, function(cfg) wesnoth.wml_actions.event(cfg) end)
|
||||
wesnoth.remove_event_handler = wesnoth.deprecate_api('wesnoth.remove_event_handler', 'wesnoth.game_events.remove', 1, nil, wesnoth.game_events.remove)
|
||||
end
|
||||
|
|
|
@ -51,5 +51,5 @@ local function on_event(eventname, priority, fcn)
|
|||
end
|
||||
end
|
||||
|
||||
core_on_event = on_event
|
||||
return on_event
|
||||
core_on_event = wesnoth.deprecate_api("on_event", "wesnoth.game_events.add", 1, nil, on_event)
|
||||
return core_on_event
|
||||
|
|
|
@ -663,6 +663,7 @@ end
|
|||
|
||||
function wml_actions.set_menu_item(cfg)
|
||||
wesnoth.interface.set_menu_item(cfg.id, cfg)
|
||||
wesnoth.game_events.add(cfg.id, wml_actions.command, true)
|
||||
end
|
||||
|
||||
function wml_actions.place_shroud(cfg)
|
||||
|
@ -732,7 +733,9 @@ function wml_actions.event(cfg)
|
|||
wesnoth.deprecated_message("[event]remove=yes", 2, "1.17.0", "Use [remove_event] instead of [event]remove=yes")
|
||||
wml_actions.remove_event(cfg)
|
||||
else
|
||||
wesnoth.add_event_handler(cfg)
|
||||
local delay = cfg.delayed_variable_substitution
|
||||
if delay == nil then delay = true end
|
||||
wesnoth.game_events.add(delay, cfg)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -89,8 +89,7 @@ bool entity_location::matches_unit(const unit_map::const_iterator & un_it) const
|
|||
* the unit is required to additionally match the unit that was supplied
|
||||
* when this was constructed.
|
||||
*/
|
||||
bool entity_location::matches_unit_filter(const unit_map::const_iterator & un_it,
|
||||
const vconfig & filter) const
|
||||
bool entity_location::matches_unit_filter(const unit_map::const_iterator & un_it, const unit_filter& filter) const
|
||||
{
|
||||
if ( !un_it.valid() )
|
||||
return false;
|
||||
|
@ -101,7 +100,7 @@ bool entity_location::matches_unit_filter(const unit_map::const_iterator & un_it
|
|||
|
||||
// Filter the unit at the filter location (should be the unit's
|
||||
// location if no special filter location was specified).
|
||||
return unit_filter(filter).matches(*un_it, filter_loc_) &&
|
||||
return filter.matches(*un_it, filter_loc_) &&
|
||||
matches_unit(un_it);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
class unit;
|
||||
class vconfig;
|
||||
|
||||
class unit_filter;
|
||||
|
||||
namespace game_events
|
||||
{
|
||||
|
@ -39,7 +39,7 @@ namespace game_events
|
|||
const map_location& filter_loc() const { return filter_loc_; }
|
||||
bool matches_unit(const unit_map::const_iterator & un_it) const;
|
||||
bool matches_unit_filter(const unit_map::const_iterator & un_it,
|
||||
const vconfig & filter) const;
|
||||
const unit_filter& filter) const;
|
||||
unit_const_ptr get_unit() const;
|
||||
|
||||
static const entity_location null_entity;
|
||||
|
|
|
@ -20,11 +20,20 @@
|
|||
*/
|
||||
|
||||
#include "game_events/handlers.hpp"
|
||||
#include "game_events/conditional_wml.hpp"
|
||||
#include "game_events/pump.hpp"
|
||||
#include "game_events/manager_impl.hpp" // for standardize_name
|
||||
|
||||
#include "formula/string_utils.hpp"
|
||||
#include "game_board.hpp"
|
||||
#include "game_data.hpp"
|
||||
#include "log.hpp"
|
||||
#include "play_controller.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "scripting/game_lua_kernel.hpp"
|
||||
#include "side_filter.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "units/filter.hpp"
|
||||
#include "variable.hpp"
|
||||
|
||||
static lg::log_domain log_engine("engine");
|
||||
|
@ -40,14 +49,44 @@ namespace game_events
|
|||
{
|
||||
/* ** event_handler ** */
|
||||
|
||||
event_handler::event_handler(config&& cfg, bool imi, const std::vector<std::string>& types, game_lua_kernel& lk)
|
||||
: first_time_only_(cfg["first_time_only"].to_bool(true))
|
||||
, is_menu_item_(imi)
|
||||
event_handler::event_handler(const std::string& types, const std::string& id)
|
||||
: first_time_only_(true)
|
||||
, is_menu_item_(false)
|
||||
, disabled_(false)
|
||||
, cfg_(cfg)
|
||||
, is_lua_(false)
|
||||
, id_(id)
|
||||
, types_(types)
|
||||
{}
|
||||
|
||||
std::vector<std::string> event_handler::names(const variable_set* vars) const
|
||||
{
|
||||
event_ref_ = lk.save_wml_event(cfg);
|
||||
std::string names = types_;
|
||||
|
||||
// Do some standardization on the name field.
|
||||
// Split the name field and standardize each one individually.
|
||||
// This ensures they're all valid for by-name lookup.
|
||||
std::vector<std::string> standardized_names;
|
||||
for(std::string single_name : utils::split(names)) {
|
||||
if(utils::might_contain_variables(single_name)) {
|
||||
if(!vars) {
|
||||
// If we don't have gamedata, we can't interpolate variables, so there's
|
||||
// no way the name will match. Move on to the next one in that case.
|
||||
continue;
|
||||
}
|
||||
single_name = utils::interpolate_variables_into_string(single_name, *vars);
|
||||
}
|
||||
// Variable interpolation could've introduced additional commas, so split again.
|
||||
for(const std::string& subname : utils::split(single_name)) {
|
||||
standardized_names.emplace_back(event_handlers::standardize_name(subname));
|
||||
}
|
||||
}
|
||||
|
||||
return standardized_names;
|
||||
}
|
||||
|
||||
bool event_handler::empty() const
|
||||
{
|
||||
return args_.empty();
|
||||
}
|
||||
|
||||
void event_handler::disable()
|
||||
|
@ -63,15 +102,150 @@ void event_handler::handle_event(const queued_event& event_info, game_lua_kernel
|
|||
}
|
||||
|
||||
if(is_menu_item_) {
|
||||
DBG_NG << cfg_["name"] << " will now invoke the following command(s):\n" << cfg_;
|
||||
DBG_NG << "menu item " << id_ << " will now invoke the following command(s):\n" << args_;
|
||||
}
|
||||
|
||||
if(first_time_only_) {
|
||||
disable();
|
||||
}
|
||||
|
||||
lk.run_wml_event(event_ref_, vconfig(cfg_, false), event_info);
|
||||
lk.run_wml_event(event_ref_, vconfig(args_, false), event_info);
|
||||
sound::commit_music_changes();
|
||||
}
|
||||
|
||||
bool event_handler::filter_event(const queued_event& ev) const
|
||||
{
|
||||
return std::all_of(filters_.begin(), filters_.end(), [&ev](const auto& filter) {
|
||||
return (*filter)(ev);
|
||||
});
|
||||
}
|
||||
|
||||
void event_handler::write_config(config &cfg) const
|
||||
{
|
||||
if(disabled_) {
|
||||
WRN_NG << "Tried to serialize disabled event, skipping";
|
||||
return;
|
||||
}
|
||||
if(is_lua_) {
|
||||
WRN_NG << "Skipping serialization of an event bound to Lua code";
|
||||
return;
|
||||
}
|
||||
if(!types_.empty()) cfg["name"] = types_;
|
||||
if(!id_.empty()) cfg["id"] = id_;
|
||||
cfg["first_time_only"] = first_time_only_;
|
||||
for(const auto& filter : filters_) {
|
||||
filter->serialize(cfg);
|
||||
}
|
||||
cfg.append(args_);
|
||||
}
|
||||
|
||||
void event_filter::serialize(config&) const
|
||||
{
|
||||
WRN_NG << "Tried to serialize an event with a filter that cannot be serialized!";
|
||||
}
|
||||
|
||||
struct filter_condition : public event_filter {
|
||||
filter_condition(const vconfig& cfg) : cfg_(cfg.make_safe()) {}
|
||||
bool operator()(const queued_event&) const override
|
||||
{
|
||||
return conditional_passed(cfg_);
|
||||
}
|
||||
void serialize(config& cfg) const override
|
||||
{
|
||||
cfg.add_child("filter_condition", cfg_.get_config());
|
||||
}
|
||||
private:
|
||||
vconfig cfg_;
|
||||
};
|
||||
|
||||
struct filter_side : public event_filter {
|
||||
filter_side(const vconfig& cfg) : ssf_(cfg.make_safe(), &resources::controller->gamestate()) {}
|
||||
bool operator()(const queued_event&) const override
|
||||
{
|
||||
return ssf_.match(resources::controller->current_side());
|
||||
}
|
||||
void serialize(config& cfg) const override
|
||||
{
|
||||
cfg.add_child("filter_side", ssf_.get_config());
|
||||
}
|
||||
private:
|
||||
side_filter ssf_;
|
||||
};
|
||||
|
||||
struct filter_unit : public event_filter {
|
||||
filter_unit(const vconfig& cfg, bool first) : suf_(cfg.make_safe()), first_(first) {}
|
||||
bool operator()(const queued_event& event_info) const override
|
||||
{
|
||||
const auto& loc = first_ ? event_info.loc1 : event_info.loc2;
|
||||
auto unit = resources::gameboard->units().find(loc);
|
||||
return loc.matches_unit_filter(unit, suf_);
|
||||
}
|
||||
void serialize(config& cfg) const override
|
||||
{
|
||||
cfg.add_child(first_ ? "filter" : "filter_second", suf_.to_config());
|
||||
}
|
||||
private:
|
||||
unit_filter suf_;
|
||||
bool first_;
|
||||
};
|
||||
|
||||
struct filter_attack : public event_filter {
|
||||
filter_attack(const vconfig& cfg, bool first) : swf_(cfg.make_safe()), first_(first) {}
|
||||
bool operator()(const queued_event& event_info) const override
|
||||
{
|
||||
const unit_map& units = resources::gameboard->units();
|
||||
const auto& loc = first_ ? event_info.loc1 : event_info.loc2;
|
||||
auto unit = units.find(loc);
|
||||
if(unit != units.end() && loc.matches_unit(unit)) {
|
||||
const config& attack = event_info.data.child(first_ ? "first" : "second");
|
||||
return swf_.empty() || matches_special_filter(attack, swf_);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void serialize(config& cfg) const override
|
||||
{
|
||||
cfg.add_child(first_ ? "filter_attack" : "filter_second_attack", swf_.get_config());
|
||||
}
|
||||
private:
|
||||
vconfig swf_;
|
||||
bool first_;
|
||||
};
|
||||
|
||||
void event_handler::read_filters(const config &cfg)
|
||||
{
|
||||
for(auto filter : cfg.all_children_range()) {
|
||||
vconfig vcfg(filter.cfg);
|
||||
if(filter.key == "filter_condition") {
|
||||
add_filter(std::make_unique<filter_condition>(vcfg));
|
||||
} else if(filter.key == "filter_side") {
|
||||
add_filter(std::make_unique<filter_side>(vcfg));
|
||||
} else if(filter.key == "filter") {
|
||||
add_filter(std::make_unique<filter_unit>(vcfg, true));
|
||||
} else if(filter.key == "filter_attack") {
|
||||
add_filter(std::make_unique<filter_attack>(vcfg, true));
|
||||
} else if(filter.key == "filter_second") {
|
||||
add_filter(std::make_unique<filter_unit>(vcfg, false));
|
||||
} else if(filter.key == "filter_second_attack") {
|
||||
add_filter(std::make_unique<filter_attack>(vcfg, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void event_handler::add_filter(std::unique_ptr<event_filter>&& filter)
|
||||
{
|
||||
filters_.push_back(std::move(filter));
|
||||
}
|
||||
|
||||
void event_handler::register_wml_event(game_lua_kernel &lk)
|
||||
{
|
||||
event_ref_ = lk.save_wml_event();
|
||||
}
|
||||
|
||||
void event_handler::set_event_ref(int idx)
|
||||
{
|
||||
event_ref_ = idx;
|
||||
is_lua_ = true;
|
||||
}
|
||||
|
||||
|
||||
} // end namespace game_events
|
||||
|
|
|
@ -30,17 +30,31 @@
|
|||
|
||||
class game_data;
|
||||
class game_lua_kernel;
|
||||
class variable_set;
|
||||
|
||||
namespace game_events
|
||||
{
|
||||
struct queued_event;
|
||||
/** Represents a single filter condition on an event. */
|
||||
struct event_filter {
|
||||
/** Runs the filter and returns whether it passes on the given event. */
|
||||
virtual bool operator()(const queued_event& event_info) const = 0;
|
||||
/** Serializes the filter into a config, if possible. */
|
||||
virtual void serialize(config& cfg) const;
|
||||
virtual ~event_filter() = default;
|
||||
event_filter() = default;
|
||||
private:
|
||||
event_filter(const event_filter&) = delete;
|
||||
event_filter& operator=(const event_filter&) = delete;
|
||||
};
|
||||
|
||||
class event_handler
|
||||
{
|
||||
public:
|
||||
event_handler(config&& cfg, bool is_menu_item, const std::vector<std::string>& types, game_lua_kernel& lk);
|
||||
event_handler(const std::string& types, const std::string& id = "");
|
||||
|
||||
const std::vector<std::string>& names() const
|
||||
std::vector<std::string> names(const variable_set* vars) const;
|
||||
const std::string& names_raw() const
|
||||
{
|
||||
return types_;
|
||||
}
|
||||
|
@ -66,18 +80,61 @@ public:
|
|||
*/
|
||||
void handle_event(const queued_event& event_info, game_lua_kernel& lk);
|
||||
|
||||
const config& get_config() const
|
||||
bool filter_event(const queued_event& event_info) const;
|
||||
|
||||
const config& arguments() const
|
||||
{
|
||||
return cfg_;
|
||||
return args_;
|
||||
}
|
||||
|
||||
const std::string& id() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
bool empty() const;
|
||||
|
||||
bool repeatable() const
|
||||
{
|
||||
return !first_time_only_;
|
||||
}
|
||||
|
||||
void write_config(config& cfg) const;
|
||||
|
||||
void set_repeatable(bool repeat = true)
|
||||
{
|
||||
first_time_only_ = !repeat;
|
||||
}
|
||||
|
||||
void set_menu_item(bool imi)
|
||||
{
|
||||
is_menu_item_ = imi;
|
||||
}
|
||||
|
||||
void set_arguments(const config& cfg)
|
||||
{
|
||||
args_ = cfg;
|
||||
}
|
||||
|
||||
void read_filters(const config& cfg);
|
||||
void add_filter(std::unique_ptr<event_filter>&& filter);
|
||||
|
||||
void register_wml_event(game_lua_kernel& lk);
|
||||
void set_event_ref(int idx);
|
||||
|
||||
private:
|
||||
bool first_time_only_;
|
||||
bool is_menu_item_;
|
||||
bool disabled_;
|
||||
config cfg_;
|
||||
/**
|
||||
* Tracks whether the event was registered from the Lua API.
|
||||
* This allows a warning to be issued in cases that will break saved games.
|
||||
*/
|
||||
bool is_lua_;
|
||||
int event_ref_;
|
||||
std::vector<std::string> types_;
|
||||
config args_;
|
||||
std::vector<std::shared_ptr<event_filter>> filters_;
|
||||
std::string id_, types_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ static lg::log_domain log_engine("engine");
|
|||
#define WRN_NG LOG_STREAM(warn, log_engine)
|
||||
|
||||
static lg::log_domain log_event_handler("event_handler");
|
||||
#define LOG_EH LOG_STREAM(info, log_event_handler)
|
||||
#define DBG_EH LOG_STREAM(debug, log_event_handler)
|
||||
|
||||
namespace
|
||||
|
@ -64,9 +65,43 @@ namespace
|
|||
namespace game_events
|
||||
{
|
||||
/** Create an event handler. */
|
||||
void manager::add_event_handler(const config& handler, game_lua_kernel& lk, bool is_menu_item)
|
||||
void manager::add_event_handler_from_wml(const config& handler, game_lua_kernel& lk, bool is_menu_item)
|
||||
{
|
||||
event_handlers_->add_event_handler(handler, lk, is_menu_item);
|
||||
auto new_handler = event_handlers_->add_event_handler(handler["name"], handler["id"], !handler["first_time_only"].to_bool(true), is_menu_item);
|
||||
if(new_handler.valid()) {
|
||||
new_handler->read_filters(handler);
|
||||
|
||||
// Strip out anything that's used by the event system itself.
|
||||
// TODO: "filter" and "formula" are stubs intended for Lua and WFL code respectively.
|
||||
config args;
|
||||
for(const auto& [attr, val] : handler.attribute_range()) {
|
||||
if(attr == "id" || attr == "name" || attr == "first_time_only" || attr.compare(0, 6, "filter") == 0) {
|
||||
continue;
|
||||
}
|
||||
args[attr] = val;
|
||||
}
|
||||
for(auto child : handler.all_children_range()) {
|
||||
if(child.key.compare(0, 6, "filter") != 0) {
|
||||
args.add_child(child.key, child.cfg);
|
||||
}
|
||||
}
|
||||
new_handler->set_arguments(args);
|
||||
new_handler->register_wml_event(lk);
|
||||
DBG_EH << "Registered WML event "
|
||||
<< (new_handler->names_raw().empty() ? "" : "'" + new_handler->names_raw() + "'")
|
||||
<< (new_handler->id().empty() ? "" : "{id=" + new_handler->id() + "}")
|
||||
<< (new_handler->repeatable() ? " (repeating" : " (first time only")
|
||||
<< (is_menu_item ? "; menu item)" : ")")
|
||||
<< " with the following actions:\n"
|
||||
<< args.debug();
|
||||
} else {
|
||||
LOG_EH << "Content of failed event:\n" << handler.debug() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
pending_event_handler manager::add_event_handler_from_lua(const std::string& name, const std::string& id, bool repeat, bool is_menu_item)
|
||||
{
|
||||
return event_handlers_->add_event_handler(name, id, repeat, is_menu_item);
|
||||
}
|
||||
|
||||
/** Removes an event handler. */
|
||||
|
@ -94,7 +129,7 @@ manager::manager()
|
|||
void manager::read_scenario(const config& scenario_cfg, game_lua_kernel& lk)
|
||||
{
|
||||
for(const config& ev : scenario_cfg.child_range("event")) {
|
||||
add_event_handler(ev, lk);
|
||||
add_event_handler_from_wml(ev, lk);
|
||||
}
|
||||
|
||||
for(const std::string& id : utils::split(scenario_cfg["unit_wml_ids"])) {
|
||||
|
@ -127,7 +162,7 @@ void manager::add_events(const config::const_child_itors& cfgs, game_lua_kernel&
|
|||
continue;
|
||||
}
|
||||
|
||||
add_event_handler(new_ev, lk);
|
||||
add_event_handler_from_wml(new_ev, lk);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +185,7 @@ void manager::write_events(config& cfg) const
|
|||
assert(!eh->disabled());
|
||||
}
|
||||
|
||||
cfg.add_child("event", eh->get_config());;
|
||||
eh->write_config(cfg.add_child("event"));
|
||||
}
|
||||
|
||||
cfg["unit_wml_ids"] = utils::join(unit_wml_ids_);
|
||||
|
@ -188,24 +223,8 @@ void manager::execute_on_events(const std::string& event_id, manager::event_func
|
|||
}
|
||||
|
||||
// Could be more than one.
|
||||
for (const std::string& name : handler->names()) {
|
||||
bool matches = false;
|
||||
|
||||
if (utils::might_contain_variables(name)) {
|
||||
// If we don't have gamedata, we can't interpolate variables, so there's
|
||||
// no way the name will match. Move on to the next one in that case.
|
||||
if (!gd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matches = standardized_event_id ==
|
||||
event_handlers::standardize_name(utils::interpolate_variables_into_string(name, *gd));
|
||||
}
|
||||
else {
|
||||
matches = standardized_event_id == name;
|
||||
}
|
||||
|
||||
if (matches) {
|
||||
for(const std::string& name : handler->names(gd)) {
|
||||
if(standardized_event_id == name) {
|
||||
func(*this, handler);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace game_events
|
|||
{
|
||||
class wml_event_pump;
|
||||
class event_handlers;
|
||||
class pending_event_handler;
|
||||
|
||||
/**
|
||||
* The game event manager loads the scenario configuration object,
|
||||
|
@ -59,8 +60,10 @@ public:
|
|||
void read_scenario(const config& scenario_cfg, game_lua_kernel& lk);
|
||||
~manager();
|
||||
|
||||
/** Create an event handler. */
|
||||
void add_event_handler(const config& handler, game_lua_kernel& lk, bool is_menu_item = false);
|
||||
/** Create an event handler from an [event] tag. */
|
||||
void add_event_handler_from_wml(const config& handler, game_lua_kernel& lk, bool is_menu_item = false);
|
||||
/** Create an empty event handler. Expects the caller to finish setting up the event. */
|
||||
pending_event_handler add_event_handler_from_lua(const std::string& name, const std::string& id, bool repeat = false, bool is_menu_item = false);
|
||||
|
||||
/** Removes an event handler. */
|
||||
void remove_event_handler(const std::string& id);
|
||||
|
|
|
@ -28,6 +28,8 @@ static lg::log_domain log_engine("engine");
|
|||
#define WRN_NG LOG_STREAM(warn, log_engine)
|
||||
|
||||
static lg::log_domain log_event_handler("event_handler");
|
||||
#define ERR_EH LOG_STREAM(err, log_event_handler)
|
||||
#define LOG_EH LOG_STREAM(info, log_event_handler)
|
||||
#define DBG_EH LOG_STREAM(debug, log_event_handler)
|
||||
|
||||
static lg::log_domain log_wml("wml");
|
||||
|
@ -49,8 +51,7 @@ void event_handlers::log_handlers()
|
|||
continue;
|
||||
}
|
||||
|
||||
const config& cfg = h->get_config();
|
||||
ss << "name=" << cfg["name"] << ", with id=" << cfg["id"] << "; ";
|
||||
ss << "name=" << h->names_raw() << ", with id=" << h->id() << "; ";
|
||||
}
|
||||
|
||||
DBG_EH << "active handlers are now " << ss.str() << "\n";
|
||||
|
@ -92,70 +93,57 @@ handler_list& event_handlers::get(const std::string& name)
|
|||
* An event with a nonempty ID will not be added if an event with that
|
||||
* ID already exists.
|
||||
*/
|
||||
void event_handlers::add_event_handler(const config& cfg, game_lua_kernel& lk, bool is_menu_item)
|
||||
pending_event_handler event_handlers::add_event_handler(const std::string& name, const std::string& id, bool repeat, bool is_menu_item)
|
||||
{
|
||||
// Someone decided to register an empty event... bail.
|
||||
if(cfg.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string name = cfg["name"];
|
||||
std::string id = cfg["id"];
|
||||
|
||||
if(!id.empty()) {
|
||||
// Ignore this handler if there is already one with this ID.
|
||||
auto find_it = id_map_.find(id);
|
||||
|
||||
if(find_it != id_map_.end() && !find_it->second.expired()) {
|
||||
DBG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "'\n";
|
||||
return;
|
||||
LOG_EH << "ignoring event handler for name='" << name << "' with id '" << id << "' because an event with that id already exists\n";
|
||||
return {*this, nullptr};
|
||||
}
|
||||
}
|
||||
|
||||
if(name.empty() && id.empty()) {
|
||||
lg::log_to_chat() << "[event] is missing name or id field\n";
|
||||
ERR_WML << "[event] is missing name or id field\n";
|
||||
DBG_WML << "Content of that event:\n" << cfg.debug() << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a copy of the event cfg here in order to do some standardization on the
|
||||
// name field. Will be moved into the handler.
|
||||
config event_cfg = cfg;
|
||||
|
||||
// Split the name field...
|
||||
std::vector<std::string> standardized_names = utils::split(name);
|
||||
|
||||
if(!name.empty()) {
|
||||
// ...and standardize each one individually. This ensures they're all valid for by-name lookup.
|
||||
for(std::string& single_name : standardized_names) {
|
||||
if(!utils::might_contain_variables(single_name)) {
|
||||
single_name = standardize_name(single_name);
|
||||
}
|
||||
static const char* msg = "[event] is missing name or id field";
|
||||
lg::log_to_chat() << msg << "\n";
|
||||
if(lg::info().dont_log(log_event_handler)) {
|
||||
ERR_EH << msg << " (run with --log-info=event_handler for more info)\n";
|
||||
} else {
|
||||
ERR_EH << msg << "\n";
|
||||
}
|
||||
|
||||
assert(!standardized_names.empty());
|
||||
|
||||
// Write the new name back to the config.
|
||||
name = utils::join(standardized_names);
|
||||
event_cfg["name"] = name;
|
||||
return {*this, nullptr};
|
||||
}
|
||||
|
||||
// Create a new handler.
|
||||
auto handler = std::make_shared<event_handler>(name, id);
|
||||
handler->set_menu_item(is_menu_item);
|
||||
handler->set_repeatable(repeat);
|
||||
return {*this, handler};
|
||||
}
|
||||
|
||||
void event_handlers::finish_adding_event_handler(handler_ptr handler)
|
||||
{
|
||||
// Someone decided to register an empty event... bail.
|
||||
if(handler->empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& names = handler->names_raw();
|
||||
const std::string& id = handler->id();
|
||||
|
||||
// Register the new handler.
|
||||
// Do note active_ holds the main shared_ptr, and the other three containers
|
||||
// construct weak_ptrs from the shared one.
|
||||
DBG_EH << "inserting event handler for name=" << name << " with id=" << id << "\n";
|
||||
active_.emplace_back(new event_handler(std::move(event_cfg), is_menu_item, standardized_names, lk));
|
||||
|
||||
//
|
||||
// !! event_cfg is invalid past this point! DO NOT USE!
|
||||
//
|
||||
DBG_EH << "inserting event handler for name=" << names << " with id=" << id << "\n";
|
||||
active_.emplace_back(handler);
|
||||
|
||||
// File by name.
|
||||
if(utils::might_contain_variables(name)) {
|
||||
if(utils::might_contain_variables(names)) {
|
||||
dynamic_.emplace_back(active_.back());
|
||||
} else {
|
||||
for(const std::string& single_name : standardized_names) {
|
||||
for(const std::string& single_name : handler->names(nullptr)) {
|
||||
by_name_[single_name].emplace_back(active_.back());
|
||||
}
|
||||
}
|
||||
|
@ -168,6 +156,11 @@ void event_handlers::add_event_handler(const config& cfg, game_lua_kernel& lk, b
|
|||
log_handlers();
|
||||
}
|
||||
|
||||
pending_event_handler::~pending_event_handler()
|
||||
{
|
||||
if(valid()) list_.finish_adding_event_handler(handler_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an event handler, identified by its ID.
|
||||
* Events with empty IDs cannot be removed.
|
||||
|
|
|
@ -25,6 +25,33 @@ class game_lua_kernel;
|
|||
|
||||
namespace game_events
|
||||
{
|
||||
class event_handlers;
|
||||
/**
|
||||
* Represents a handler that is about to be added to the events manager but is still waiting for some data.
|
||||
* The handler will automatically be added when this class is destroyed, unless it has become invalid somehow.
|
||||
*/
|
||||
class pending_event_handler
|
||||
{
|
||||
event_handlers& list_;
|
||||
handler_ptr handler_;
|
||||
pending_event_handler(const pending_event_handler&) = delete;
|
||||
pending_event_handler& operator=(const pending_event_handler&) = delete;
|
||||
// It's move-constructible, but there's no way to make it move-assignable since it contains a reference...
|
||||
pending_event_handler& operator=(pending_event_handler&&) = delete;
|
||||
public:
|
||||
pending_event_handler(event_handlers& list, handler_ptr handler)
|
||||
: list_(list)
|
||||
, handler_(handler)
|
||||
{}
|
||||
/** Check if this handler is valid. */
|
||||
bool valid() const {return handler_.get();}
|
||||
/** Access the event handler. */
|
||||
event_handler* operator->() {return handler_.get();}
|
||||
event_handler& operator*() {return *handler_;}
|
||||
pending_event_handler(pending_event_handler&&) = default;
|
||||
~pending_event_handler();
|
||||
};
|
||||
|
||||
// event_handlers is essentially the implementation details of the manager
|
||||
class event_handlers
|
||||
{
|
||||
|
@ -51,6 +78,9 @@ private:
|
|||
|
||||
void log_handlers();
|
||||
|
||||
friend pending_event_handler;
|
||||
void finish_adding_event_handler(handler_ptr new_handler);
|
||||
|
||||
public:
|
||||
/** Utility to standardize the event names used in by_name_. */
|
||||
static std::string standardize_name(const std::string& name);
|
||||
|
@ -84,7 +114,7 @@ public:
|
|||
handler_list& get(const std::string& name);
|
||||
|
||||
/** Adds an event handler. */
|
||||
void add_event_handler(const config& cfg, game_lua_kernel& lk, bool is_menu_item = false);
|
||||
pending_event_handler add_event_handler(const std::string& name, const std::string& id, bool repeat, bool is_menu_item = false);
|
||||
|
||||
/** Removes an event handler, identified by its ID. */
|
||||
void remove_event_handler(const std::string& id);
|
||||
|
|
|
@ -209,7 +209,7 @@ void wml_menu_item::init_handler(game_lua_kernel& lk)
|
|||
// If this menu item has a [command], add a handler for it.
|
||||
if(!command_.empty()) {
|
||||
assert(resources::game_events);
|
||||
resources::game_events->add_event_handler(command_, lk, true);
|
||||
resources::game_events->add_event_handler_from_wml(command_, lk, true);
|
||||
}
|
||||
|
||||
// Hotkey support
|
||||
|
@ -338,16 +338,14 @@ void wml_menu_item::update(const vconfig& vcfg)
|
|||
void wml_menu_item::update_command(const config& new_command)
|
||||
{
|
||||
// If there is an old command, remove it from the event handlers.
|
||||
if(!command_.empty()) {
|
||||
assert(resources::game_events);
|
||||
assert(resources::game_events);
|
||||
|
||||
resources::game_events->execute_on_events(event_name_, [&](game_events::manager& man, handler_ptr& ptr) {
|
||||
if(ptr->is_menu_item()) {
|
||||
LOG_NG << "Removing command for " << event_name_ << ".\n";
|
||||
man.remove_event_handler(command_["id"].str());
|
||||
}
|
||||
});
|
||||
}
|
||||
resources::game_events->execute_on_events(event_name_, [&](game_events::manager& man, handler_ptr& ptr) {
|
||||
if(ptr->is_menu_item()) {
|
||||
LOG_NG << "Removing command for " << event_name_ << ".\n";
|
||||
man.remove_event_handler(command_["id"].str());
|
||||
}
|
||||
});
|
||||
|
||||
// Update our stored command.
|
||||
if(new_command.empty()) {
|
||||
|
@ -368,7 +366,7 @@ void wml_menu_item::update_command(const config& new_command)
|
|||
LOG_NG << "Setting command for " << event_name_ << " to:\n" << command_;
|
||||
assert(resources::game_events);
|
||||
assert(resources::lua_kernel);
|
||||
resources::game_events->add_event_handler(command_, *resources::lua_kernel, true);
|
||||
resources::game_events->add_event_handler_from_wml(command_, *resources::lua_kernel, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
*/
|
||||
|
||||
#include "game_events/pump.hpp"
|
||||
#include "game_events/conditional_wml.hpp"
|
||||
#include "game_events/handlers.hpp"
|
||||
|
||||
#include "display_chat_manager.hpp"
|
||||
|
@ -186,84 +185,6 @@ pump_manager::~pump_manager()
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff the given event passes all its filters.
|
||||
*/
|
||||
bool wml_event_pump::filter_event(const event_handler& handler, const queued_event& ev)
|
||||
{
|
||||
const unit_map& units = resources::gameboard->units();
|
||||
unit_map::const_iterator unit1 = units.find(ev.loc1);
|
||||
unit_map::const_iterator unit2 = units.find(ev.loc2);
|
||||
vconfig filters(handler.get_config());
|
||||
|
||||
for(const vconfig& condition : filters.get_children("filter_condition")) {
|
||||
if(!conditional_passed(condition)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for(const vconfig& f : filters.get_children("filter_side")) {
|
||||
side_filter ssf(f, &resources::controller->gamestate());
|
||||
if(!ssf.match(resources::controller->current_side()))
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const vconfig& f : filters.get_children("filter")) {
|
||||
if(!ev.loc1.matches_unit_filter(unit1, f)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
vconfig::child_list special_filters = filters.get_children("filter_attack");
|
||||
bool special_matches = special_filters.empty();
|
||||
if(!special_matches && unit1 != units.end()) {
|
||||
const bool matches_unit = ev.loc1.matches_unit(unit1);
|
||||
const config& attack = ev.data.child("first");
|
||||
for(const vconfig& f : special_filters) {
|
||||
if(f.empty()) {
|
||||
special_matches = true;
|
||||
} else if(!matches_unit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
special_matches = special_matches || matches_special_filter(attack, f);
|
||||
}
|
||||
}
|
||||
|
||||
if(!special_matches) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const vconfig& f : filters.get_children("filter_second")) {
|
||||
if(!ev.loc2.matches_unit_filter(unit2, f)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
special_filters = filters.get_children("filter_second_attack");
|
||||
special_matches = special_filters.empty();
|
||||
if(!special_matches && unit2 != units.end()) {
|
||||
const bool matches_unit = ev.loc2.matches_unit(unit2);
|
||||
const config& attack = ev.data.child("second");
|
||||
for(const vconfig& f : special_filters) {
|
||||
if(f.empty()) {
|
||||
special_matches = true;
|
||||
} else if(!matches_unit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
special_matches = special_matches || matches_special_filter(attack, f);
|
||||
}
|
||||
}
|
||||
|
||||
if(!special_matches) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All filters passed.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an event through a single event handler.
|
||||
* This includes checking event filters, but not checking that the event
|
||||
|
@ -289,7 +210,7 @@ void wml_event_pump::process_event(handler_ptr& handler_p, const queued_event& e
|
|||
scoped_weapon_info first_weapon("weapon", ev.data.child("first"));
|
||||
scoped_weapon_info second_weapon("second_weapon", ev.data.child("second"));
|
||||
|
||||
if(!filter_event(*handler_p, ev)) {
|
||||
if(!handler_p->filter_event(ev)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -572,7 +493,7 @@ pump_result_t wml_event_pump::operator()()
|
|||
if(event_id.empty()) {
|
||||
// Handle events of this name.
|
||||
impl_->my_manager->execute_on_events(event_name, [&](game_events::manager&, handler_ptr& ptr) {
|
||||
DBG_EH << "processing event " << event_name << " with id=" << ptr->get_config()["id"] << "\n";
|
||||
DBG_EH << "processing event " << event_name << " with id=" << ptr->id() << "\n";
|
||||
|
||||
// Let this handler process our event.
|
||||
process_event(ptr, ev);
|
||||
|
@ -582,7 +503,7 @@ pump_result_t wml_event_pump::operator()()
|
|||
handler_ptr cur_handler = impl_->my_manager->get_event_handler_by_id(event_id);
|
||||
|
||||
if(cur_handler) {
|
||||
DBG_EH << "processing event " << event_name << " with id=" << cur_handler->get_config()["id"] << "\n";
|
||||
DBG_EH << "processing event " << event_name << " with id=" << cur_handler->id() << "\n";
|
||||
process_event(cur_handler, ev);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,8 +143,6 @@ public:
|
|||
void flush_messages();
|
||||
|
||||
private:
|
||||
bool filter_event(const event_handler& handler, const queued_event& ev);
|
||||
|
||||
void process_event(handler_ptr& handler_p, const queued_event& ev);
|
||||
|
||||
void fill_wml_messages_map(std::map<std::string, int>& msg_map, std::stringstream& source);
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
#include "game_errors.hpp" // for game_error
|
||||
#include "game_events/conditional_wml.hpp" // for conditional_passed
|
||||
#include "game_events/entity_location.hpp"
|
||||
#include "game_events/handlers.hpp"
|
||||
#include "game_events/manager_impl.hpp" // for pending_event_handler
|
||||
#include "game_events/pump.hpp" // for queued_event
|
||||
#include "preferences/game.hpp" // for encountered_units
|
||||
#include "log.hpp" // for LOG_STREAM, logger, etc
|
||||
|
@ -3689,16 +3691,106 @@ int game_lua_kernel::intf_log_replay(lua_State* L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/** Adding new events */
|
||||
static std::string read_event_name(lua_State* L, int idx)
|
||||
{
|
||||
if(lua_isstring(L, idx)) {
|
||||
return lua_tostring(L, idx);
|
||||
} else {
|
||||
return utils::join(lua_check<std::vector<std::string>>(L, idx));
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new event handler
|
||||
* Arg 1: Table of options.
|
||||
* name: Event to handle, as a string or list of strings
|
||||
* id: Event ID
|
||||
* menu_item: True if this is a menu item (an ID is required); this means removing the menu item will automatically remove this event. Default false.
|
||||
* first_time_only: Whether this event should fire again after the first time; default true.
|
||||
* filter: Event filters as a config with filter tags, a table of the form {filter_type = filter_contents}, or a function
|
||||
* content: The content of the event. This is a WML table passed verbatim into the event when it fires. If no function is specified, it will be interpreted as ActionWML.
|
||||
* action: The function to call when the event triggers. Defaults to wesnoth.wml_actions.command.
|
||||
* OR
|
||||
* Arg 1: Event to handle, as a string or list of strings; or menu item ID if this is a menu item
|
||||
* Arg 2: The function to call when the event triggers
|
||||
* Arg 3: True if this is a menu item
|
||||
* OR
|
||||
* Arg 1: A boolean - whether to delay variable substitution
|
||||
* Arg 2: A full event specification as a WML config
|
||||
*/
|
||||
int game_lua_kernel::intf_add_event(lua_State *L)
|
||||
{
|
||||
vconfig cfg(luaW_checkvconfig(L, 1));
|
||||
game_events::manager & man = *game_state_.events_manager_;
|
||||
|
||||
if (!cfg["delayed_variable_substitution"].to_bool(true)) {
|
||||
man.add_event_handler(cfg.get_parsed_config(), *this);
|
||||
if(lua_isboolean(L, 1)) {
|
||||
bool delayed_variable_substitution = luaW_toboolean(L, 1);
|
||||
vconfig cfg(luaW_checkvconfig(L, 2));
|
||||
if(delayed_variable_substitution) {
|
||||
man.add_event_handler_from_wml(cfg.get_config(), *this);
|
||||
} else {
|
||||
man.add_event_handler_from_wml(cfg.get_parsed_config(), *this);
|
||||
}
|
||||
} else if(lua_isstring(L, 1)) {
|
||||
bool is_menu_item = luaW_toboolean(L, 3), repeat = false;
|
||||
std::string name = read_event_name(L, 1), id;
|
||||
if(is_menu_item) {
|
||||
id = name;
|
||||
name = "menu item " + name;
|
||||
}
|
||||
auto new_handler = man.add_event_handler_from_lua(name, id, repeat, is_menu_item);
|
||||
if(new_handler.valid()) {
|
||||
// An event with empty arguments is not added, so set some dummy arguments
|
||||
new_handler->set_arguments(config{"__quick_lua_event", true});
|
||||
new_handler->set_event_ref(save_wml_event(2));
|
||||
}
|
||||
} else if(lua_istable(L, 1)) {
|
||||
using namespace std::literals;
|
||||
std::string name, id = luaW_table_get_def(L, 1, "id", ""s);
|
||||
bool repeat = !luaW_table_get_def(L, 1, "first_time_only", true), is_menu_item = luaW_table_get_def(L, 1, "menu_item", false);
|
||||
if(luaW_tableget(L, 1, "name")) {
|
||||
name = read_event_name(L, -1);
|
||||
}
|
||||
auto new_handler = man.add_event_handler_from_lua(name, id, repeat, is_menu_item);
|
||||
if(new_handler.valid()) {
|
||||
new_handler->set_arguments(luaW_table_get_def(L, 1, "content", config{"__empty_lua_event", true}));
|
||||
|
||||
if(luaW_tableget(L, 1, "filter")) {
|
||||
int filterIdx = lua_gettop(L);
|
||||
config filters;
|
||||
if(!luaW_toconfig(L, filterIdx, filters)) {
|
||||
if(lua_isfunction(L, filterIdx)) {
|
||||
//new_handler->add_filter(std::make_unique<lua_event_filter>());
|
||||
} else {
|
||||
if(luaW_tableget(L, filterIdx, "condition")) {
|
||||
filters.add_child("filter_condition", luaW_checkconfig(L, -1));
|
||||
}
|
||||
if(luaW_tableget(L, filterIdx, "side")) {
|
||||
filters.add_child("filter_side", luaW_checkconfig(L, -1));
|
||||
}
|
||||
if(luaW_tableget(L, filterIdx, "unit")) {
|
||||
filters.add_child("filter", luaW_checkconfig(L, -1));
|
||||
}
|
||||
if(luaW_tableget(L, filterIdx, "attack")) {
|
||||
filters.add_child("filter_attack", luaW_checkconfig(L, -1));
|
||||
}
|
||||
if(luaW_tableget(L, filterIdx, "second_unit")) {
|
||||
filters.add_child("filter_second", luaW_checkconfig(L, -1));
|
||||
}
|
||||
if(luaW_tableget(L, filterIdx, "second_attack")) {
|
||||
filters.add_child("filter_second_attack", luaW_checkconfig(L, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
new_handler->read_filters(filters);
|
||||
}
|
||||
|
||||
if(luaW_tableget(L, 1, "action")) {
|
||||
new_handler->set_event_ref(save_wml_event(-1));
|
||||
} else {
|
||||
new_handler->register_wml_event(*this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
man.add_event_handler(cfg.get_config(), *this);
|
||||
return luaW_type_error(L, 1, "boolean, string, or table");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -4499,7 +4591,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "get_era", &intf_get_era },
|
||||
{ "get_resource", &intf_get_resource },
|
||||
{ "modify_ai", &intf_modify_ai_old },
|
||||
{ "add_event_handler", &dispatch<&game_lua_kernel::intf_add_event > },
|
||||
{ "allow_undo", &dispatch<&game_lua_kernel::intf_allow_undo > },
|
||||
{ "cancel_action", &dispatch<&game_lua_kernel::intf_cancel_action > },
|
||||
{ "fire_event", &dispatch2<&game_lua_kernel::intf_fire_event, false > },
|
||||
|
@ -4507,7 +4598,6 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
{ "log_replay", &dispatch<&game_lua_kernel::intf_log_replay > },
|
||||
{ "log", &dispatch<&game_lua_kernel::intf_log > },
|
||||
{ "redraw", &dispatch<&game_lua_kernel::intf_redraw > },
|
||||
{ "remove_event_handler", &dispatch<&game_lua_kernel::intf_remove_event > },
|
||||
{ "simulate_combat", &dispatch<&game_lua_kernel::intf_simulate_combat > },
|
||||
{ nullptr, nullptr }
|
||||
};lua_getglobal(L, "wesnoth");
|
||||
|
@ -4845,10 +4935,15 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
lua_pop(L, 1);
|
||||
|
||||
// Create the game_events table.
|
||||
cmd_log_ << "Adding game_events table...\n";
|
||||
|
||||
cmd_log_ << "Adding game_events module...\n";
|
||||
static luaL_Reg const event_callbacks[] {
|
||||
{ "add", &dispatch<&game_lua_kernel::intf_add_event> },
|
||||
{ "remove", &dispatch<&game_lua_kernel::intf_remove_event> },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
lua_getglobal(L, "wesnoth");
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, event_callbacks, 0);
|
||||
lua_setfield(L, -2, "game_events");
|
||||
lua_pop(L, 1);
|
||||
|
||||
|
@ -4893,6 +4988,10 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
|
|||
lua_rawset(L, -3);
|
||||
}
|
||||
lua_settop(L, 0);
|
||||
|
||||
// Set up the registry table for event handlers
|
||||
lua_newtable(L);
|
||||
EVENT_TABLE = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
void game_lua_kernel::initialize(const config& level)
|
||||
|
@ -5238,38 +5337,54 @@ static int intf_run_event_wml(lua_State* L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int game_lua_kernel::save_wml_event(const config& evt)
|
||||
int game_lua_kernel::save_wml_event()
|
||||
{
|
||||
lua_State* L = mState;
|
||||
if(EVENT_TABLE == LUA_NOREF) {
|
||||
lua_newtable(L);
|
||||
EVENT_TABLE = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
|
||||
int evtIdx = lua_gettop(L);
|
||||
ON_SCOPE_EXIT(L) {
|
||||
lua_pop(L, 1);
|
||||
};
|
||||
if(evt.has_attribute("code")) {
|
||||
std::ostringstream name;
|
||||
name << "event ";
|
||||
if(evt.has_attribute("name")) {
|
||||
name << evt["name"];
|
||||
} else {
|
||||
name << "<anon>";
|
||||
}
|
||||
if(evt.has_attribute("id")) {
|
||||
name << "[id=" << evt["id"] << "]";
|
||||
}
|
||||
if(!load_string(evt["code"].str().c_str(), name.str())) {
|
||||
ERR_LUA << "Failed to register WML event: " << name.str();
|
||||
return LUA_NOREF;
|
||||
}
|
||||
lua_pushcfunction(L, intf_run_event_wml);
|
||||
return luaL_ref(L, evtIdx);
|
||||
}
|
||||
|
||||
int game_lua_kernel::save_wml_event(const std::string& name, const std::string& id, const std::string& code)
|
||||
{
|
||||
lua_State* L = mState;
|
||||
lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
|
||||
int evtIdx = lua_gettop(L);
|
||||
ON_SCOPE_EXIT(L) {
|
||||
lua_pop(L, 1);
|
||||
};
|
||||
std::ostringstream lua_name;
|
||||
lua_name << "event ";
|
||||
if(name.empty()) {
|
||||
lua_name << "<anon>";
|
||||
} else {
|
||||
lua_pushcfunction(L, intf_run_event_wml);
|
||||
lua_name << name;
|
||||
}
|
||||
int ref = luaL_ref(L, evtIdx);
|
||||
return ref;
|
||||
if(!id.empty()) {
|
||||
lua_name << "[id=" << id << "]";
|
||||
}
|
||||
if(!load_string(code.c_str(), lua_name.str())) {
|
||||
ERR_LUA << "Failed to register WML event: " << lua_name.str();
|
||||
return LUA_NOREF;
|
||||
}
|
||||
return luaL_ref(L, evtIdx);
|
||||
}
|
||||
|
||||
int game_lua_kernel::save_wml_event(int idx)
|
||||
{
|
||||
lua_State* L = mState;
|
||||
idx = lua_absindex(L, idx);
|
||||
lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
|
||||
int evtIdx = lua_gettop(L);
|
||||
ON_SCOPE_EXIT(L) {
|
||||
lua_pop(L, 1);
|
||||
};
|
||||
lua_pushvalue(L, idx);
|
||||
return luaL_ref(L, evtIdx);
|
||||
}
|
||||
|
||||
void game_lua_kernel::clear_wml_event(int ref)
|
||||
|
|
|
@ -219,11 +219,40 @@ public:
|
|||
bool run_filter(char const *name, const team& t);
|
||||
bool run_filter(char const *name, int nArgs);
|
||||
bool run_wml_conditional(const std::string&, const vconfig&);
|
||||
/** Store a WML event in the Lua registry, as a function */
|
||||
int save_wml_event(const config& evt);
|
||||
/** Clear a WML event store in the Lua registry */
|
||||
/**
|
||||
* Store a WML event in the Lua registry, as a function.
|
||||
* Uses a default function that interprets ActionWML.
|
||||
* @return A unique index into the EVENT_TABLE within the Lua registry
|
||||
*/
|
||||
int save_wml_event();
|
||||
/**
|
||||
* Store a WML event in the Lua registry, as a function.
|
||||
* Compiles the function from the given code.
|
||||
* @param name The event name, used to generate a chunk name for the compiled function
|
||||
* @param id The event id, used to generate a chunk name for the compiled function
|
||||
* @param code The actual code of the function
|
||||
* @return A unique index into the EVENT_TABLE within the Lua registry
|
||||
*/
|
||||
int save_wml_event(const std::string& name, const std::string& id, const std::string& code);
|
||||
/**
|
||||
* Store a WML event in the Lua registry, as a function.
|
||||
* Uses the function at the specified Lua stack index.
|
||||
* @param idx The Lua stack index of the function to store
|
||||
* @return A unique index into the EVENT_TABLE within the Lua registry
|
||||
*/
|
||||
int save_wml_event(int idx);
|
||||
/**
|
||||
* Clear a WML event store in the Lua registry.
|
||||
* @param ref The unique index into the EVENT_TABLE within the Lua registry
|
||||
*/
|
||||
void clear_wml_event(int ref);
|
||||
/** Run a WML store in the Lua registry */
|
||||
/**
|
||||
* Run a WML stored in the Lua registry.
|
||||
* @param ref The unique index into the EVENT_TABLE within the Lua registry
|
||||
* @param args Arguments to pass to the event function, as a config
|
||||
* @param ev The event data for the event being fired
|
||||
* @return Whether the function was successfully called; could be false if @a ref was invalid or if the function raised an error
|
||||
*/
|
||||
bool run_wml_event(int ref, const vconfig& args, const game_events::queued_event& ev);
|
||||
|
||||
virtual void log_error(char const* msg, char const* context = "Lua error") override;
|
||||
|
|
|
@ -82,6 +82,12 @@ namespace lua_check_impl
|
|||
return luaL_checkstring(L, n);
|
||||
}
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_same_v<T, std::string>, std::string>
|
||||
lua_to_or_default(lua_State *L, int n, const T& def)
|
||||
{
|
||||
return luaL_optstring(L, n, def.c_str());
|
||||
}
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_same_v<T, std::string>, void>
|
||||
lua_push(lua_State *L, const T& val)
|
||||
{
|
||||
|
@ -116,6 +122,13 @@ namespace lua_check_impl
|
|||
return luaW_checkconfig(L, n);
|
||||
}
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_same_v<T, config>, config>
|
||||
lua_to_or_default(lua_State *L, int n, const T& def)
|
||||
{
|
||||
config cfg;
|
||||
return luaW_toconfig(L, n, cfg) ? cfg : def;
|
||||
}
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_same_v<T, config>, void>
|
||||
lua_push(lua_State *L, const config& val)
|
||||
{
|
||||
|
|
|
@ -40,6 +40,7 @@ public:
|
|||
bool match(const team& t) const;
|
||||
bool match(const int side) const;
|
||||
std::vector<int> get_teams() const;
|
||||
const config& get_config() const {return cfg_.get_config();}
|
||||
|
||||
private:
|
||||
side_filter(const side_filter &other);
|
||||
|
|
Loading…
Add table
Reference in a new issue