Lua API: Add synced_command and end_turn actions to the Game plugin context

This commit is contained in:
Celtic Minstrel 2024-09-23 01:17:36 -04:00
parent f793d8ef7e
commit c746e38cc9
5 changed files with 60 additions and 1 deletions

View file

@ -51,6 +51,7 @@
#include "savegame.hpp"
#include "scripting/game_lua_kernel.hpp"
#include "scripting/plugins/context.hpp"
#include "scripting/plugins/manager.hpp"
#include "sound.hpp"
#include "soundsource.hpp"
#include "statistics.hpp"
@ -282,6 +283,57 @@ void play_controller::init(const config& level)
plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
plugins_context_->set_callback_execute(*resources::lua_kernel);
plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
plugins_context_->set_accessor_int("current_side", [this](config) { return current_side(); });
plugins_context_->set_accessor_int("current_turn", [this](config) { return turn(); });
plugins_context_->set_accessor_bool("can_move", [this](config) { return !events::commands_disabled && gamestate().gamedata_.phase() == game_data::TURN_PLAYING; });
plugins_context_->set_callback("end_turn", [this](config) { require_end_turn(); }, false);
plugins_context_->set_callback("synced_command", [this](config cmd) {
auto& pm = *plugins_manager::get();
if(resources::whiteboard->has_planned_unit_map())
{
ERR_NG << "plugin called synced command while whiteboard is applied, ignoring";
pm.notify_event("synced_command_error", config{"error", "whiteboard"});
return;
}
auto& gamedata = gamestate().gamedata_;
const bool is_too_early = gamedata.phase() == game_data::INITIAL || resources::gamedata->phase() == game_data::PRELOAD;
const bool is_during_turn = gamedata.phase() == game_data::TURN_PLAYING;
const bool is_unsynced = synced_context::get_synced_state() == synced_context::UNSYNCED;
if(is_too_early) {
ERR_NG << "synced command called too early, only allowed at START or later";
pm.notify_event("synced_command_error", config{"error", "too-early"});
return;
}
if(is_unsynced && !is_during_turn) {
ERR_NG << "synced command can only be used during a turn when a user would also be able to invoke commands";
pm.notify_event("synced_command_error", config{"error", "not-your-turn"});
return;
}
if(is_unsynced && events::commands_disabled) {
ERR_NG << "synced command cannot be invoked while commands are blocked";
pm.notify_event("synced_command_error", config{"error", "disabled"});
return;
}
if(is_unsynced && !resources::controller->current_team().is_local()) {
ERR_NG << "synced command can only be used from clients that control the currently playing side";
pm.notify_event("synced_command_error", config{"error", "not-your-turn"});
return;
}
for(const auto [key, child] : cmd.all_children_range()) {
synced_context::run_in_synced_context_if_not_already(
/*commandname*/ key,
/*data*/ child,
/*use_undo*/ true,
/*show*/ true,
/*error_handler*/ [&pm](const std::string& message) {
ERR_NG << "synced command from plugin raised an error: " << message;
pm.notify_event("synced_command_error", config{"error", "error", "message", message});
}
);
ai::manager::get_singleton().raise_gamestate_changed();
}
}, false);
});
}

View file

@ -98,6 +98,7 @@ public:
void init_side_end();
virtual void force_end_turn() = 0;
virtual void require_end_turn() = 0;
virtual void check_objectives() = 0;
virtual void on_not_observer() = 0;

View file

@ -74,7 +74,7 @@ public:
void end_turn();
void force_end_turn() override;
void require_end_turn();
void require_end_turn() override;
class hotkey_handler;
std::string describe_result() const;

View file

@ -81,6 +81,11 @@ void plugins_context::set_accessor_int(const std::string & name, std::function<i
set_accessor(name, [func, name](const config& cfg) { return config {name, func(cfg)}; });
}
void plugins_context::set_accessor_bool(const std::string & name, std::function<bool(config)> func)
{
set_accessor(name, [func, name](const config& cfg) { return config {name, func(cfg)}; });
}
std::size_t plugins_context::erase_accessor(const std::string & name)
{

View file

@ -54,6 +54,7 @@ public:
void set_accessor(const std::string & name, accessor_function);
void set_accessor_string(const std::string & name, std::function<std::string(config)>); //helpers which create a config from a simple type
void set_accessor_int(const std::string & name, std::function<int(config)>);
void set_accessor_bool(const std::string & name, std::function<bool(config)>);
std::size_t erase_accessor(const std::string & name);
std::size_t clear_accessors();