Events are now a Lua function instead of WML

The function is specified by [event]code= and will be passed the entire contents of the [event] tag (including the code itself).

If code= is omitted, a default is supplied which runs ActionWML; however, internally it is still a function rather than a list of ActionWML commands.
This commit is contained in:
Celtic Minstrel 2021-04-02 17:03:51 -04:00 committed by Celtic Minstrel
parent 30df6fe4ff
commit 11e5700848
14 changed files with 115 additions and 31 deletions

View file

@ -40,13 +40,14 @@ namespace game_events
{
/* ** event_handler ** */
event_handler::event_handler(config&& cfg, bool imi, const std::vector<std::string>& types)
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)
, disabled_(false)
, cfg_(cfg)
, types_(types)
{
event_ref_ = lk.save_wml_event(cfg);
}
void event_handler::disable()
@ -69,7 +70,7 @@ void event_handler::handle_event(const queued_event& event_info, game_lua_kernel
disable();
}
lk.run_wml_action("command", vconfig(cfg_, false), event_info);
lk.run_wml_event(event_ref_, vconfig(cfg_, false), event_info);
sound::commit_music_changes();
}

View file

@ -38,7 +38,7 @@ struct queued_event;
class event_handler
{
public:
event_handler(config&& cfg, bool is_menu_item, const std::vector<std::string>& types);
event_handler(config&& cfg, bool is_menu_item, const std::vector<std::string>& types, game_lua_kernel& lk);
const std::vector<std::string>& names() const
{
@ -76,6 +76,7 @@ private:
bool is_menu_item_;
bool disabled_;
config cfg_;
int event_ref_;
std::vector<std::string> types_;
};

View file

@ -64,9 +64,9 @@ namespace
namespace game_events
{
/** Create an event handler. */
void manager::add_event_handler(const config& handler, bool is_menu_item)
void manager::add_event_handler(const config& handler, game_lua_kernel& lk, bool is_menu_item)
{
event_handlers_->add_event_handler(handler, is_menu_item);
event_handlers_->add_event_handler(handler, lk, is_menu_item);
}
/** Removes an event handler. */
@ -91,10 +91,10 @@ manager::manager()
{
}
void manager::read_scenario(const config& scenario_cfg)
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);
add_event_handler(ev, lk);
}
for(const std::string& id : utils::split(scenario_cfg["unit_wml_ids"])) {
@ -104,14 +104,14 @@ void manager::read_scenario(const config& scenario_cfg)
wml_menu_items_.set_menu_items(scenario_cfg);
// Create the event handlers for menu items.
wml_menu_items_.init_handlers();
wml_menu_items_.init_handlers(lk);
}
manager::~manager()
{
}
void manager::add_events(const config::const_child_itors& cfgs, const std::string& type)
void manager::add_events(const config::const_child_itors& cfgs, game_lua_kernel& lk, const std::string& type)
{
if(!type.empty()) {
if(std::find(unit_wml_ids_.begin(), unit_wml_ids_.end(), type) != unit_wml_ids_.end()) {
@ -127,7 +127,7 @@ void manager::add_events(const config::const_child_itors& cfgs, const std::strin
continue;
}
add_event_handler(new_ev);
add_event_handler(new_ev, lk);
}
}

View file

@ -25,6 +25,7 @@
class filter_context;
class game_data;
class game_lua_kernel;
namespace game_events
{
@ -55,11 +56,11 @@ public:
manager& operator=(const manager&) = delete;
explicit manager();
void read_scenario(const config& scenario_cfg);
void read_scenario(const config& scenario_cfg, game_lua_kernel& lk);
~manager();
/** Create an event handler. */
void add_event_handler(const config& handler, bool is_menu_item = false);
void add_event_handler(const config& handler, game_lua_kernel& lk, bool is_menu_item = false);
/** Removes an event handler. */
void remove_event_handler(const std::string& id);
@ -67,7 +68,7 @@ public:
/** Gets an event handler by ID */
const handler_ptr get_event_handler_by_id(const std::string& id);
void add_events(const config::const_child_itors& cfgs, const std::string& type = std::string());
void add_events(const config::const_child_itors& cfgs, game_lua_kernel& lk, const std::string& type = std::string());
void write_events(config& cfg) const;

View file

@ -92,7 +92,7 @@ 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, bool is_menu_item)
void event_handlers::add_event_handler(const config& cfg, game_lua_kernel& lk, bool is_menu_item)
{
// Someone decided to register an empty event... bail.
if(cfg.empty()) {
@ -145,7 +145,7 @@ void event_handlers::add_event_handler(const config& cfg, bool is_menu_item)
// 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));
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!

View file

@ -21,6 +21,8 @@
#include <deque>
#include <unordered_map>
class game_lua_kernel;
namespace game_events
{
// event_handlers is essentially the implementation details of the manager
@ -82,7 +84,7 @@ public:
handler_list& get(const std::string& name);
/** Adds an event handler. */
void add_event_handler(const config& cfg, bool is_menu_item = false);
void add_event_handler(const config& cfg, game_lua_kernel& lk, bool is_menu_item = false);
/** Removes an event handler, identified by its ID. */
void remove_event_handler(const std::string& id);

View file

@ -204,12 +204,12 @@ void wml_menu_item::finish_handler()
}
}
void wml_menu_item::init_handler()
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_, true);
resources::game_events->add_event_handler(command_, lk, true);
}
// Hotkey support
@ -367,7 +367,8 @@ void wml_menu_item::update_command(const config& new_command)
// Register the event.
LOG_NG << "Setting command for " << event_name_ << " to:\n" << command_;
assert(resources::game_events);
resources::game_events->add_event_handler(command_, true);
assert(resources::lua_kernel);
resources::game_events->add_event_handler(command_, *resources::lua_kernel, true);
}
}

View file

@ -28,6 +28,7 @@
class filter_context;
class game_data;
class game_lua_kernel;
struct map_location;
namespace game_events
@ -109,7 +110,7 @@ public:
void finish_handler();
/** Initializes the implicit event handler for an inlined [command]. */
void init_handler();
void init_handler(game_lua_kernel& lk);
/**
* The text to put in a menu for this item.

View file

@ -163,7 +163,7 @@ wmi_manager::item_ptr wmi_manager::get_item(const std::string& id) const
/**
* Initializes the implicit event handlers for inlined [command]s.
*/
void wmi_manager::init_handlers() const
void wmi_manager::init_handlers(game_lua_kernel& lk) const
{
// Applying default hotkeys here currently does not work because
// the hotkeys are reset by play_controler::init_managers() ->
@ -179,7 +179,7 @@ void wmi_manager::init_handlers() const
// Loop through each menu item.
for(const auto& item : wml_menu_items_) {
// If this menu item has a [command], add a handler for it.
item.second->init_handler();
item.second->init_handler(lk);
// Count the menu items (for the diagnostic message).
++wmi_count;

View file

@ -28,6 +28,7 @@
class config;
class filter_context;
class game_data;
class game_lua_kernel;
struct map_location;
class unit_map;
class vconfig;
@ -79,7 +80,7 @@ public:
unit_map& units) const;
/** Initializes the implicit event handlers for inlined [command]s. */
void init_handlers() const;
void init_handlers(game_lua_kernel& lk) const;
void to_config(config& cfg) const;

View file

@ -94,7 +94,7 @@ game_state::game_state(const config& level, play_controller& pc, game_board& boa
, first_human_team_(-1)
{
lua_kernel_->load_core();
events_manager_->read_scenario(level);
events_manager_->read_scenario(level, *lua_kernel_);
if(const config& endlevel_cfg = level.child("end_level_data")) {
end_level_data el_data;
el_data.read(endlevel_cfg);
@ -178,7 +178,7 @@ void game_state::place_sides_in_preferred_locations(const config& level)
void game_state::init(const config& level, play_controller & pc)
{
events_manager_->read_scenario(level);
events_manager_->read_scenario(level, *lua_kernel_);
gui2::dialogs::loading_screen::progress(loading_stage::init_teams);
if (level["modify_placing"].to_bool()) {
LOG_NG << "modifying placing..." << std::endl;

View file

@ -103,6 +103,7 @@
#include "units/map.hpp" // for unit_map, etc
#include "units/ptr.hpp" // for unit_const_ptr, unit_ptr
#include "units/types.hpp" // for unit_type_data, unit_types, etc
#include "utils/scope_exit.hpp"
#include "variable.hpp" // for vconfig, etc
#include "variable_info.hpp"
#include "whiteboard/manager.hpp" // for whiteboard
@ -3695,9 +3696,9 @@ int game_lua_kernel::intf_add_event(lua_State *L)
game_events::manager & man = *game_state_.events_manager_;
if (!cfg["delayed_variable_substitution"].to_bool(true)) {
man.add_event_handler(cfg.get_parsed_config());
man.add_event_handler(cfg.get_parsed_config(), *this);
} else {
man.add_event_handler(cfg.get_config());
man.add_event_handler(cfg.get_config(), *this);
}
return 0;
}
@ -4481,6 +4482,7 @@ game_lua_kernel::game_lua_kernel(game_state & gs, play_controller & pc, reports
, play_controller_(pc)
, reports_(reports_object)
, level_lua_()
, EVENT_TABLE(LUA_NOREF)
, queued_events_()
, map_locked_(0)
{
@ -5225,6 +5227,73 @@ bool game_lua_kernel::run_wml_conditional(const std::string& cmd, const vconfig&
return b;
}
static int intf_run_event_wml(lua_State* L)
{
int argIdx = lua_gettop(L);
if(!luaW_getglobal(L, "wesnoth", "wml_actions", "command")) {
return luaL_error(L, "wesnoth.wml_actions.command is missing");
}
lua_pushvalue(L, argIdx);
lua_call(L, 1, 0);
return 0;
}
int game_lua_kernel::save_wml_event(const config& evt)
{
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;
}
} else {
lua_pushcfunction(L, intf_run_event_wml);
}
int ref = luaL_ref(L, evtIdx);
return ref;
}
void game_lua_kernel::clear_wml_event(int ref)
{
lua_State* L = mState;
lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
luaL_unref(L, -1, ref);
lua_pop(L, 1);
}
bool game_lua_kernel::run_wml_event(int ref, const vconfig& args, const game_events::queued_event& ev)
{
lua_State* L = mState;
lua_geti(L, LUA_REGISTRYINDEX, EVENT_TABLE);
ON_SCOPE_EXIT(L) {
lua_pop(L, 1);
};
lua_geti(L, -1, ref);
if(lua_isnil(L, -1)) return false;
luaW_pushvconfig(L, args);
queued_event_context dummy(&ev, queued_events_);
return protected_call(1, 0);
}
/**
* Runs a script from a location filter.

View file

@ -58,6 +58,7 @@ class game_lua_kernel : public lua_kernel_base
tod_manager & tod_man();
config level_lua_;
int EVENT_TABLE;
std::stack<game_events::queued_event const * > queued_events_;
@ -218,6 +219,12 @@ 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 */
void clear_wml_event(int ref);
/** Run a WML store in the Lua registry */
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;

View file

@ -421,8 +421,8 @@ void unit::init(const config& cfg, bool use_traits, const vconfig* vcfg)
}
}
if(resources::game_events) {
resources::game_events->add_events(events_.child_range("event"));
if(resources::game_events && resources::lua_kernel) {
resources::game_events->add_events(events_.child_range("event"), *resources::lua_kernel);
}
random_traits_ = cfg["random_traits"].to_bool(true);
@ -997,8 +997,8 @@ void unit::advance_to(const unit_type& u_type, bool use_traits)
}
// In case the unit carries EventWML, apply it now
if(resources::game_events) {
resources::game_events->add_events(new_type.events(), new_type.id());
if(resources::game_events && resources::lua_kernel) {
resources::game_events->add_events(new_type.events(), *resources::lua_kernel, new_type.id());
}
bool bool_small_profile = get_attr_changed(UA_SMALL_PROFILE);
bool bool_profile = get_attr_changed(UA_PROFILE);