diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 8e30a850b6..5857815266 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -259,6 +259,22 @@ declare global { result: RideCreateGameActionResult; } + interface SmallSceneryPlaceEventArgs extends GameActionEventArgs { + readonly x: number; + readonly y: number; + readonly z: number; + readonly direction: number; + readonly quadrant: number; + readonly object: number; + readonly primaryColour: number; + readonly secondaryColour: number; + } + + interface GuestSetNameActionEventArgs extends GameActionEventArgs { + readonly id: number; + readonly name: number; + } + interface NetworkEventArgs { readonly player: number; } diff --git a/src/openrct2/actions/GameAction.h b/src/openrct2/actions/GameAction.h index 79ef489294..a883485c06 100644 --- a/src/openrct2/actions/GameAction.h +++ b/src/openrct2/actions/GameAction.h @@ -140,6 +140,39 @@ public: std::string GetErrorMessage() const; }; +/** + * + */ +class GameActionParameterVisitor +{ +public: + virtual ~GameActionParameterVisitor() = default; + + virtual void Visit(const std::string_view& name, int32_t& param) + { + } + + virtual void Visit(const std::string_view& name, std::string& param) + { + } + + void Visit(CoordsXYZD& param) + { + Visit("x", param.x); + Visit("y", param.y); + Visit("z", param.z); + Visit("direction", param.direction); + } + + template void Visit(const std::string_view& name, T& param) + { + static_assert(std::is_arithmetic::value, "Not an arithmetic type"); + auto value = static_cast(param); + Visit(name, value); + param = static_cast(value); + } +}; + struct GameAction { public: @@ -164,6 +197,10 @@ public: virtual const char* GetName() const = 0; + virtual void AcceptParameters(GameActionParameterVisitor&) + { + } + NetworkPlayerId_t GetPlayer() const { return _playerId; diff --git a/src/openrct2/actions/GuestSetNameAction.hpp b/src/openrct2/actions/GuestSetNameAction.hpp index b8baa6543a..57d85e0788 100644 --- a/src/openrct2/actions/GuestSetNameAction.hpp +++ b/src/openrct2/actions/GuestSetNameAction.hpp @@ -37,6 +37,22 @@ public: { } + void AcceptParameters(GameActionParameterVisitor & visitor) override + { + visitor.Visit("id", _spriteIndex); + visitor.Visit("name", _name); + } + + uint16_t GetSpriteIndex() const + { + return _spriteIndex; + } + + std::string GetGuestName() const + { + return _name; + } + uint16_t GetActionFlags() const override { return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; diff --git a/src/openrct2/actions/ParkSetNameAction.hpp b/src/openrct2/actions/ParkSetNameAction.hpp index fc158f27fd..eab6302c53 100644 --- a/src/openrct2/actions/ParkSetNameAction.hpp +++ b/src/openrct2/actions/ParkSetNameAction.hpp @@ -37,6 +37,11 @@ public: { } + void AcceptParameters(GameActionParameterVisitor & visitor) override + { + visitor.Visit("name", _name); + } + uint16_t GetActionFlags() const override { return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; diff --git a/src/openrct2/actions/RideCreateAction.hpp b/src/openrct2/actions/RideCreateAction.hpp index 5b13f591e6..bf708a91f7 100644 --- a/src/openrct2/actions/RideCreateAction.hpp +++ b/src/openrct2/actions/RideCreateAction.hpp @@ -60,6 +60,14 @@ public: { } + void AcceptParameters(GameActionParameterVisitor & visitor) override + { + visitor.Visit("rideType", _rideType); + visitor.Visit("rideObject", _subType); + visitor.Visit("colour1", _colour1); + visitor.Visit("colour2", _colour2); + } + int32_t GetRideType() const { return _rideType; diff --git a/src/openrct2/actions/SmallSceneryPlaceAction.hpp b/src/openrct2/actions/SmallSceneryPlaceAction.hpp index 1223b5dd6d..51039faf1a 100644 --- a/src/openrct2/actions/SmallSceneryPlaceAction.hpp +++ b/src/openrct2/actions/SmallSceneryPlaceAction.hpp @@ -73,6 +73,15 @@ public: { } + void AcceptParameters(GameActionParameterVisitor & visitor) override + { + visitor.Visit(_loc); + visitor.Visit("quadrant", _quadrant); + visitor.Visit("object", _sceneryType); + visitor.Visit("primaryColour", _primaryColour); + visitor.Visit("secondaryColour", _secondaryColour); + } + uint32_t GetCooldownTime() const override { return 20; diff --git a/src/openrct2/scripting/ScContext.hpp b/src/openrct2/scripting/ScContext.hpp index 928c37490d..5b1e1d9878 100644 --- a/src/openrct2/scripting/ScContext.hpp +++ b/src/openrct2/scripting/ScContext.hpp @@ -11,9 +11,7 @@ #ifdef ENABLE_SCRIPTING -# include "../actions/CustomAction.hpp" -# include "../actions/ParkSetNameAction.hpp" -# include "../actions/SmallSceneryPlaceAction.hpp" +# include "../actions/GameAction.h" # include "../object/ObjectManager.h" # include "../scenario/Scenario.h" # include "Duktape.hpp" @@ -161,7 +159,7 @@ namespace OpenRCT2::Scripting auto ctx = scriptEngine.GetContext(); try { - auto action = CreateGameAction(actionid, args); + auto action = scriptEngine.CreateGameAction(actionid, args); if (action != nullptr) { auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin(); @@ -189,45 +187,6 @@ namespace OpenRCT2::Scripting } } - std::unique_ptr CreateGameAction(const std::string& actionid, const DukValue& args) - { - if (actionid == "parksetname") - { - auto name = args["name"].as_string(); - return std::make_unique(name); - } - else if (actionid == "smallsceneryplace") - { - CoordsXYZD loc; - loc.x = args["x"].as_int(); - loc.y = args["y"].as_int(); - loc.z = args["z"].as_int(); - loc.direction = args["direction"].as_int(); - uint8_t quadrant = args["quadrant"].as_int(); - uint8_t sceneryType = args["object"].as_int(); - uint8_t primaryColour = args["primaryColour"].as_int(); - uint8_t secondaryColour = args["secondaryColour"].as_int(); - return std::make_unique(loc, quadrant, sceneryType, primaryColour, secondaryColour); - } - else - { - // Serialise args to json so that it can be sent - auto ctx = args.context(); - if (args.type() == DukValue::Type::OBJECT) - { - args.push(); - } - else - { - duk_push_object(ctx); - } - auto jsonz = duk_json_encode(ctx, -1); - auto json = std::string(jsonz); - duk_pop(ctx); - return std::make_unique(actionid, json); - } - } - void HandleGameActionResult( const std::shared_ptr& plugin, const GameActionResult& res, const DukValue& callback) { diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 8b67b46b98..541f979d04 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -12,6 +12,7 @@ # include "ScriptEngine.h" # include "../PlatformEnvironment.h" +# include "../actions/CustomAction.hpp" # include "../actions/GameAction.h" # include "../actions/RideCreateAction.hpp" # include "../config/Config.h" @@ -811,7 +812,7 @@ bool ScriptEngine::RegisterCustomAction( return false; } - CustomAction customAction; + CustomActionInfo customAction; customAction.Owner = plugin; customAction.Name = std::move(actionz); customAction.Query = query; @@ -835,6 +836,52 @@ void ScriptEngine::RemoveCustomGameActions(const std::shared_ptr& plugin } } +class DukToGameActionParameterVisitor : public GameActionParameterVisitor +{ +private: + DukValue _dukValue; + +public: + DukToGameActionParameterVisitor(DukValue&& dukValue) + : _dukValue(dukValue) + { + } + + void Visit(const std::string_view& name, int32_t& param) override + { + param = _dukValue[name].as_int(); + } + + void Visit(const std::string_view& name, std::string& param) override + { + param = _dukValue[name].as_string(); + } +}; + +class DukFromGameActionParameterVisitor : public GameActionParameterVisitor +{ +private: + DukObject& _dukObject; + +public: + DukFromGameActionParameterVisitor(DukObject& dukObject) + : _dukObject(dukObject) + { + } + + void Visit(const std::string_view& name, int32_t& param) override + { + std::string szName(name); + _dukObject.Set(szName.c_str(), param); + } + + void Visit(const std::string_view& name, std::string& param) override + { + std::string szName(name); + _dukObject.Set(szName.c_str(), param); + } +}; + void ScriptEngine::RunGameActionHooks(const GameAction& action, std::unique_ptr& result, bool isExecute) { DukStackFrame frame(_context); @@ -848,12 +895,8 @@ void ScriptEngine::RunGameActionHooks(const GameAction& action, std::unique_ptr< auto flags = action.GetActionFlags(); obj.Set("isClientOnly", (flags & GA_FLAGS::CLIENT_ONLY) != 0); - if (action.GetType() == GAME_COMMAND_CREATE_RIDE) - { - auto& rideCreateAction = static_cast(action); - obj.Set("rideType", rideCreateAction.GetRideType()); - obj.Set("rideObject", rideCreateAction.GetRideObject()); - } + DukFromGameActionParameterVisitor visitor(obj); + const_cast(action).AcceptParameters(visitor); obj.Set("result", GameActionResultToDuk(action, result)); auto dukEventArgs = obj.Take(); @@ -873,6 +916,51 @@ void ScriptEngine::RunGameActionHooks(const GameAction& action, std::unique_ptr< } } +static std::unique_ptr CreateGameActionFromActionId(const std::string& actionid) +{ + const static std::unordered_map ActionNameToType = { + { "parksetname", GAME_COMMAND_SET_PARK_NAME }, + { "smallsceneryplace", GAME_COMMAND_PLACE_SCENERY }, + { "guestsetname", GAME_COMMAND_SET_GUEST_NAME }, + }; + + auto result = ActionNameToType.find(actionid); + if (result != ActionNameToType.end()) + { + return GameActions::Create(result->second); + } + return nullptr; +} + +std::unique_ptr ScriptEngine::CreateGameAction(const std::string& actionid, const DukValue& args) +{ + auto action = CreateGameActionFromActionId(actionid); + if (action != nullptr) + { + DukValue argsCopy = args; + DukToGameActionParameterVisitor visitor(std::move(argsCopy)); + action->AcceptParameters(visitor); + return action; + } + else + { + // Serialise args to json so that it can be sent + auto ctx = args.context(); + if (args.type() == DukValue::Type::OBJECT) + { + args.push(); + } + else + { + duk_push_object(ctx); + } + auto jsonz = duk_json_encode(ctx, -1); + auto json = std::string(jsonz); + duk_pop(ctx); + return std::make_unique(actionid, json); + } +} + void ScriptEngine::InitSharedStorage() { duk_push_object(_context); diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index 23e18756ad..0acd6a1329 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -123,7 +123,7 @@ namespace OpenRCT2::Scripting std::mutex _changedPluginFilesMutex; std::vector)>> _pluginStoppedSubscriptions; - struct CustomAction + struct CustomActionInfo { std::shared_ptr Owner; std::string Name; @@ -131,7 +131,7 @@ namespace OpenRCT2::Scripting DukValue Execute; }; - std::unordered_map _customActions; + std::unordered_map _customActions; public: ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env); @@ -181,6 +181,7 @@ namespace OpenRCT2::Scripting const std::shared_ptr& plugin, const std::string_view& action, const DukValue& query, const DukValue& execute); void RunGameActionHooks(const GameAction& action, std::unique_ptr& result, bool isExecute); + std::unique_ptr CreateGameAction(const std::string& actionid, const DukValue& args); void SaveSharedStorage();