From 3e39e2d0db779deab33168b78e55b8e23f53185e Mon Sep 17 00:00:00 2001 From: relic-automaton <65099288+relic-automaton@users.noreply.github.com> Date: Sun, 10 May 2020 19:26:10 -0700 Subject: [PATCH] [Plugin] Add a script hook for ride ratings calculation (#11698) The hook is called after ride rating calculation is complete and allows scripts to override the ratings. --- distribution/openrct2.d.ts | 15 +++++++++--- src/openrct2/ride/RideRatings.cpp | 35 +++++++++++++++++++++++++++ src/openrct2/scripting/HookEngine.cpp | 20 +++++++-------- src/openrct2/scripting/HookEngine.h | 1 + 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index c5b50226ff..5f2561f04c 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -35,7 +35,7 @@ declare global { var network: Network; /** APIs for the park and management of it. */ var park: Park; - /** + /** * APIs for controlling the user interface. * These will only be available to servers and clients that are not running headless mode. * Plugin writers should check if ui is available using `typeof ui !== 'undefined'`. @@ -114,7 +114,7 @@ declare global { * Executes a command using the legacy console REPL. This should not be used * by plugins, and exists only for servers to continue using old commands until * all functionality can be accomplished with this scripting API. - * + * * @deprecated * @param command The command and arguments to execute. */ @@ -213,6 +213,7 @@ declare global { subscribe(hook: "network.authenticate", callback: (e: NetworkAuthenticateEventArgs) => void): IDisposable; subscribe(hook: "network.join", callback: (e: NetworkEventArgs) => void): IDisposable; subscribe(hook: "network.leave", callback: (e: NetworkEventArgs) => void): IDisposable; + subscribe(hook: "ride.ratings.calculate", callback: (e: RideRatingsCalculateArgs) => void): IDisposable; } interface Configuration { @@ -277,7 +278,8 @@ declare global { type HookType = "interval.tick" | "interval.day" | - "network.chat" | "network.action" | "network.join" | "network.leave"; + "network.chat" | "network.action" | "network.join" | "network.leave" | + "ride.ratings.calculate"; type ExpenditureType = "ride_construction" | @@ -331,6 +333,13 @@ declare global { cancel: boolean; } + interface RideRatingsCalculateArgs { + readonly rideId: number; + excitement: number; + intensity: number; + nausea: number; + } + /** * APIs for the in-game date. */ diff --git a/src/openrct2/ride/RideRatings.cpp b/src/openrct2/ride/RideRatings.cpp index 4bd648b5ee..d12ec006b8 100644 --- a/src/openrct2/ride/RideRatings.cpp +++ b/src/openrct2/ride/RideRatings.cpp @@ -10,9 +10,11 @@ #include "RideRatings.h" #include "../Cheats.h" +#include "../Context.h" #include "../OpenRCT2.h" #include "../interface/Window.h" #include "../localisation/Date.h" +#include "../scripting/ScriptEngine.h" #include "../world/Footpath.h" #include "../world/Map.h" #include "../world/Surface.h" @@ -24,6 +26,9 @@ #include #include +using namespace OpenRCT2; +using namespace OpenRCT2::Scripting; + enum { RIDE_RATINGS_STATE_FIND_NEXT_RIDE, @@ -762,6 +767,36 @@ static void ride_ratings_calculate(Ride* ride) ride->ratings.nausea = max(0, ride->ratings.nausea); } #endif + +#ifdef ENABLE_SCRIPTING + auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); + if (hookEngine.HasSubscriptions(HOOK_TYPE::RIDE_RATINGS_CALCULATE)) + { + auto ctx = GetContext()->GetScriptEngine().GetContext(); + auto originalExcitement = ride->excitement; + auto originalIntensity = ride->intensity; + auto originalNausea = ride->nausea; + + // Create event args object + auto obj = DukObject(ctx); + obj.Set("rideId", ride->id); + obj.Set("excitement", originalExcitement); + obj.Set("intensity", originalIntensity); + obj.Set("nausea", originalNausea); + + // Call the subscriptions + auto e = obj.Take(); + hookEngine.Call(HOOK_TYPE::RIDE_RATINGS_CALCULATE, e, true); + + auto scriptExcitement = AsOrDefault(e["excitement"], static_cast(originalExcitement)); + auto scriptIntensity = AsOrDefault(e["intensity"], static_cast(originalIntensity)); + auto scriptNausea = AsOrDefault(e["nausea"], static_cast(originalNausea)); + + ride->excitement = std::clamp(scriptExcitement, 0, INT16_MAX); + ride->intensity = std::clamp(scriptIntensity, 0, INT16_MAX); + ride->nausea = std::clamp(scriptNausea, 0, INT16_MAX); + } +#endif } static void ride_ratings_calculate_value(Ride* ride) diff --git a/src/openrct2/scripting/HookEngine.cpp b/src/openrct2/scripting/HookEngine.cpp index bfed9d0b76..c3dd601a54 100644 --- a/src/openrct2/scripting/HookEngine.cpp +++ b/src/openrct2/scripting/HookEngine.cpp @@ -19,16 +19,16 @@ using namespace OpenRCT2::Scripting; HOOK_TYPE OpenRCT2::Scripting::GetHookType(const std::string& name) { - static const std::unordered_map LookupTable({ - { "action.query", HOOK_TYPE::ACTION_QUERY }, - { "action.execute", HOOK_TYPE::ACTION_EXECUTE }, - { "interval.tick", HOOK_TYPE::INTERVAL_TICK }, - { "interval.day", HOOK_TYPE::INTERVAL_DAY }, - { "network.chat", HOOK_TYPE::NETWORK_CHAT }, - { "network.authenticate", HOOK_TYPE::NETWORK_AUTHENTICATE }, - { "network.join", HOOK_TYPE::NETWORK_JOIN }, - { "network.leave", HOOK_TYPE::NETWORK_LEAVE }, - }); + static const std::unordered_map LookupTable( + { { "action.query", HOOK_TYPE::ACTION_QUERY }, + { "action.execute", HOOK_TYPE::ACTION_EXECUTE }, + { "interval.tick", HOOK_TYPE::INTERVAL_TICK }, + { "interval.day", HOOK_TYPE::INTERVAL_DAY }, + { "network.chat", HOOK_TYPE::NETWORK_CHAT }, + { "network.authenticate", HOOK_TYPE::NETWORK_AUTHENTICATE }, + { "network.join", HOOK_TYPE::NETWORK_JOIN }, + { "network.leave", HOOK_TYPE::NETWORK_LEAVE }, + { "ride.ratings.calculate", HOOK_TYPE::RIDE_RATINGS_CALCULATE } }); auto result = LookupTable.find(name); return (result != LookupTable.end()) ? result->second : HOOK_TYPE::UNDEFINED; } diff --git a/src/openrct2/scripting/HookEngine.h b/src/openrct2/scripting/HookEngine.h index 896cbe7292..9b504d2cc4 100644 --- a/src/openrct2/scripting/HookEngine.h +++ b/src/openrct2/scripting/HookEngine.h @@ -36,6 +36,7 @@ namespace OpenRCT2::Scripting NETWORK_AUTHENTICATE, NETWORK_JOIN, NETWORK_LEAVE, + RIDE_RATINGS_CALCULATE, COUNT, UNDEFINED = -1, };