From 84fdd44e6a5eaf705a170f7c7bae9332ad7ff6a9 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 20 Feb 2022 02:05:24 +0000 Subject: [PATCH] Add map changed hook --- distribution/openrct2.d.ts | 7 ++++++- src/openrct2-ui/title/TitleSequencePlayer.cpp | 12 ++++++++++-- src/openrct2-ui/windows/TitleMenu.cpp | 1 + src/openrct2-ui/windows/TopToolbar.cpp | 1 + src/openrct2/Context.cpp | 1 + src/openrct2/Game.cpp | 12 ++++++++++++ src/openrct2/Game.h | 1 + src/openrct2/network/NetworkBase.cpp | 2 ++ src/openrct2/scripting/HookEngine.cpp | 10 ++++++++++ src/openrct2/scripting/HookEngine.h | 2 ++ src/openrct2/scripting/bindings/game/ScContext.hpp | 5 +++++ src/openrct2/title/TitleScreen.cpp | 1 + 12 files changed, 52 insertions(+), 3 deletions(-) diff --git a/distribution/openrct2.d.ts b/distribution/openrct2.d.ts index 5a62087759..d935a75867 100644 --- a/distribution/openrct2.d.ts +++ b/distribution/openrct2.d.ts @@ -287,6 +287,11 @@ declare global { subscribe(hook: "vehicle.crash", callback: (e: VehicleCrashArgs) => void): IDisposable; subscribe(hook: "map.save", callback: () => void): IDisposable; + /** + * Can only be used in intransient plugins. + */ + subscribe(hook: "map.changed", callback: () => void): IDisposable; + /** * Registers a function to be called every so often in realtime, specified by the given delay. * @param callback The function to call every time the delay has elapsed. @@ -387,7 +392,7 @@ declare global { "interval.tick" | "interval.day" | "network.chat" | "network.action" | "network.join" | "network.leave" | "ride.ratings.calculate" | "action.location" | "vehicle.crash" | - "map.save"; + "map.changed" | "map.save"; type ExpenditureType = "ride_construction" | diff --git a/src/openrct2-ui/title/TitleSequencePlayer.cpp b/src/openrct2-ui/title/TitleSequencePlayer.cpp index 4426219a59..8050409ade 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.cpp +++ b/src/openrct2-ui/title/TitleSequencePlayer.cpp @@ -288,7 +288,11 @@ private: { loadSuccess = LoadParkFromStream(parkHandle->Stream.get(), parkHandle->HintPath); } - if (!loadSuccess) + if (loadSuccess) + { + game_notify_map_changed(); + } + else { if (_sequence->Saves.size() > saveIndex) { @@ -307,7 +311,11 @@ private: { loadSuccess = LoadParkFromFile(scenario->path); } - if (!loadSuccess) + if (loadSuccess) + { + game_notify_map_changed(); + } + else { Console::Error::WriteLine("Failed to load: \"%s\" for the title sequence.", command.Scenario); return false; diff --git a/src/openrct2-ui/windows/TitleMenu.cpp b/src/openrct2-ui/windows/TitleMenu.cpp index 25684f0af8..8698db5f15 100644 --- a/src/openrct2-ui/windows/TitleMenu.cpp +++ b/src/openrct2-ui/windows/TitleMenu.cpp @@ -108,6 +108,7 @@ static void WindowTitleMenuScenarioselectCallback(const utf8* path) { OpenRCT2::GetContext()->LoadParkFromFile(path, false, true); game_load_scripts(); + game_notify_map_changed(); } static void WindowTitleMenuMouseup(rct_window* w, rct_widgetindex widgetIndex) diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index f176e0e469..02bdfe8490 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -537,6 +537,7 @@ static void WindowTopToolbarScenarioselectCallback(const utf8* path) window_close_by_class(WC_EDITOR_OBJECT_SELECTION); GetContext()->LoadParkFromFile(path, false, true); game_load_scripts(); + game_notify_map_changed(); } /** diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 4ae0c71197..49be7bf598 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -918,6 +918,7 @@ namespace OpenRCT2 #endif // DISABLE_NETWORK { game_load_scripts(); + game_notify_map_changed(); } break; } diff --git a/src/openrct2/Game.cpp b/src/openrct2/Game.cpp index 433a10f67a..85bfb40460 100644 --- a/src/openrct2/Game.cpp +++ b/src/openrct2/Game.cpp @@ -513,6 +513,17 @@ void game_unload_scripts() #endif } +void game_notify_map_changed() +{ +#ifdef ENABLE_SCRIPTING + using namespace OpenRCT2::Scripting; + + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto& hookEngine = scriptEngine.GetHookEngine(); + hookEngine.Call(HOOK_TYPE::MAP_CHANGE, false); +#endif +} + /** * * rct2: 0x0069E9A7 @@ -696,6 +707,7 @@ static void game_load_or_quit_no_save_prompt_callback(int32_t result, const utf8 window_close_by_class(WC_EDITOR_OBJECT_SELECTION); context_load_park_from_file(path); game_load_scripts(); + game_notify_map_changed(); } } diff --git a/src/openrct2/Game.h b/src/openrct2/Game.h index c76a99a936..1e8465e114 100644 --- a/src/openrct2/Game.h +++ b/src/openrct2/Game.h @@ -160,6 +160,7 @@ void load_from_sv6(const char* path); void game_load_init(); void game_load_scripts(); void game_unload_scripts(); +void game_notify_map_changed(); void pause_toggle(); bool game_is_paused(); bool game_is_not_paused(); diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index b547149912..026cfc27c9 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -403,6 +403,7 @@ bool NetworkBase::BeginServer(uint16_t port, const std::string& address) _advertiser = CreateServerAdvertiser(listening_port); game_load_scripts(); + game_notify_map_changed(); return true; } @@ -2688,6 +2689,7 @@ void NetworkBase::Client_Handle_MAP([[maybe_unused]] NetworkConnection& connecti { game_load_init(); game_load_scripts(); + game_notify_map_changed(); _serverState.tick = gCurrentTicks; // window_network_status_open("Loaded new map from network"); _serverState.state = NetworkServerState::Ok; diff --git a/src/openrct2/scripting/HookEngine.cpp b/src/openrct2/scripting/HookEngine.cpp index 33525160d9..a1f9d2302d 100644 --- a/src/openrct2/scripting/HookEngine.cpp +++ b/src/openrct2/scripting/HookEngine.cpp @@ -31,6 +31,7 @@ static const EnumMap HooksLookupTable({ { "action.location", HOOK_TYPE::ACTION_LOCATION }, { "guest.generation", HOOK_TYPE::GUEST_GENERATION }, { "vehicle.crash", HOOK_TYPE::VEHICLE_CRASH }, + { "map.change", HOOK_TYPE::MAP_CHANGE }, { "map.save", HOOK_TYPE::MAP_SAVE }, }); @@ -97,6 +98,15 @@ bool HookEngine::HasSubscriptions(HOOK_TYPE type) const return !hookList.Hooks.empty(); } +bool HookEngine::IsValidHookForPlugin(HOOK_TYPE type, Plugin& plugin) const +{ + if (type == HOOK_TYPE::MAP_CHANGE && plugin.GetMetadata().Type != PluginType::Intransient) + { + return false; + } + return true; +} + void HookEngine::Call(HOOK_TYPE type, bool isGameStateMutable) { auto& hookList = GetHookList(type); diff --git a/src/openrct2/scripting/HookEngine.h b/src/openrct2/scripting/HookEngine.h index dde0f4de1b..832388ace2 100644 --- a/src/openrct2/scripting/HookEngine.h +++ b/src/openrct2/scripting/HookEngine.h @@ -40,6 +40,7 @@ namespace OpenRCT2::Scripting ACTION_LOCATION, GUEST_GENERATION, VEHICLE_CRASH, + MAP_CHANGE, MAP_SAVE, COUNT, UNDEFINED = -1, @@ -87,6 +88,7 @@ namespace OpenRCT2::Scripting void UnsubscribeAll(std::shared_ptr owner); void UnsubscribeAll(); bool HasSubscriptions(HOOK_TYPE type) const; + bool IsValidHookForPlugin(HOOK_TYPE type, Plugin& plugin) const; void Call(HOOK_TYPE type, bool isGameStateMutable); void Call(HOOK_TYPE type, const DukValue& arg, bool isGameStateMutable); void Call( diff --git a/src/openrct2/scripting/bindings/game/ScContext.hpp b/src/openrct2/scripting/bindings/game/ScContext.hpp index 5b5592a848..2dc64f7a0c 100644 --- a/src/openrct2/scripting/bindings/game/ScContext.hpp +++ b/src/openrct2/scripting/bindings/game/ScContext.hpp @@ -278,6 +278,11 @@ namespace OpenRCT2::Scripting duk_error(ctx, DUK_ERR_ERROR, "Not in a plugin context"); } + if (!_hookEngine.IsValidHookForPlugin(hookType, *owner)) + { + duk_error(ctx, DUK_ERR_ERROR, "Hook type not available for this plugin type."); + } + auto cookie = _hookEngine.Subscribe(hookType, owner, callback); return std::make_shared([this, hookType, cookie]() { _hookEngine.Unsubscribe(hookType, cookie); }); } diff --git a/src/openrct2/title/TitleScreen.cpp b/src/openrct2/title/TitleScreen.cpp index 7fffc73097..7131969c9e 100644 --- a/src/openrct2/title/TitleScreen.cpp +++ b/src/openrct2/title/TitleScreen.cpp @@ -336,6 +336,7 @@ bool TitleScreen::TryLoadSequence(bool loadPreview) if (!loadPreview) { GetContext()->GetGameState()->InitAll(DEFAULT_MAP_SIZE); + game_notify_map_changed(); } return false; }