diff --git a/src/play_controller.cpp b/src/play_controller.cpp index 6c2aa4e391e..229f46ba9fc 100644 --- a/src/play_controller.cpp +++ b/src/play_controller.cpp @@ -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); }); } diff --git a/src/play_controller.hpp b/src/play_controller.hpp index b84f0a4b0ec..3cf30707c2a 100644 --- a/src/play_controller.hpp +++ b/src/play_controller.hpp @@ -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; diff --git a/src/playsingle_controller.hpp b/src/playsingle_controller.hpp index 225b5aeded3..f4658ca09c5 100644 --- a/src/playsingle_controller.hpp +++ b/src/playsingle_controller.hpp @@ -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; diff --git a/src/scripting/plugins/context.cpp b/src/scripting/plugins/context.cpp index aeabff3a734..4ca46eb5de3 100644 --- a/src/scripting/plugins/context.cpp +++ b/src/scripting/plugins/context.cpp @@ -81,6 +81,11 @@ void plugins_context::set_accessor_int(const std::string & name, std::function 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) { diff --git a/src/scripting/plugins/context.hpp b/src/scripting/plugins/context.hpp index 505f840466e..4cffe477023 100644 --- a/src/scripting/plugins/context.hpp +++ b/src/scripting/plugins/context.hpp @@ -54,6 +54,7 @@ public: void set_accessor(const std::string & name, accessor_function); void set_accessor_string(const std::string & name, std::function); //helpers which create a config from a simple type void set_accessor_int(const std::string & name, std::function); + void set_accessor_bool(const std::string & name, std::function); std::size_t erase_accessor(const std::string & name); std::size_t clear_accessors();