From ab00d23d26bfd5287cb9129f75a3a951a645fbee Mon Sep 17 00:00:00 2001 From: Stephan Spengler Date: Sun, 17 Mar 2024 13:02:43 +0100 Subject: [PATCH] create or delete banners when tile elements are changed by plugins --- distribution/changelog.txt | 1 + src/openrct2/network/NetworkBase.cpp | 2 +- src/openrct2/scripting/ScriptEngine.h | 2 +- .../scripting/bindings/world/ScTile.cpp | 8 ++ .../bindings/world/ScTileElement.cpp | 134 +++++++++++++++++- .../bindings/world/ScTileElement.hpp | 5 + 6 files changed, 149 insertions(+), 3 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 6dcbc5f64f70..38800cab15ed 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -66,6 +66,7 @@ - Fix: [#21434] Number of guests overflows in objective text. - Fix: [#21522] Supports for 3×3 turns and 45 degree turns on the Hybrid Coaster and Wooden Roller Coaster not drawn correctly. - Fix: [#21543] Crash with creating a TrackIterator with invalid arguments. +- Fix: [#21627] [Plugin] Banners are properly created or deleted when tile elements are changed by plugins. - Fix: [#21635] Tile inspector hotkey can set wall slope for non-slopeable objects. - Fix: [#21641] Crash when creating track iterator from an invalid tile element. - Fix: [#21652] Dialog window to confirm overwriting files does not apply the theme colours correctly. diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 32fe00ee790e..31fe61140778 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -46,7 +46,7 @@ using namespace OpenRCT2; // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -constexpr uint8_t kNetworkStreamVersion = 0; +constexpr uint8_t kNetworkStreamVersion = 1; const std::string kNetworkStreamID = std::string(OPENRCT2_VERSION) + "-" + std::to_string(kNetworkStreamVersion); diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index cc3b892a0db1..0fe500334825 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -47,7 +47,7 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 87; + static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 88; // Versions marking breaking changes. static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33; diff --git a/src/openrct2/scripting/bindings/world/ScTile.cpp b/src/openrct2/scripting/bindings/world/ScTile.cpp index 1eece1c9c9ee..913a7a5c9350 100644 --- a/src/openrct2/scripting/bindings/world/ScTile.cpp +++ b/src/openrct2/scripting/bindings/world/ScTile.cpp @@ -15,6 +15,7 @@ # include "../../../common.h" # include "../../../core/Guard.hpp" # include "../../../entity/EntityRegistry.h" +# include "../../../object/LargeSceneryEntry.h" # include "../../../ride/Track.h" # include "../../../world/Footpath.h" # include "../../../world/Scenery.h" @@ -195,6 +196,13 @@ namespace OpenRCT2::Scripting auto first = GetFirstElement(); if (index < GetNumElements(first)) { + auto element = &first[index]; + if (element->GetType() != TileElementType::LargeScenery + || element->AsLargeScenery()->GetEntry()->scrolling_mode == SCROLLING_MODE_NONE + || ScTileElement::GetOtherLargeSceneryElement(_coords, element->AsLargeScenery()) == nullptr) + { + element->RemoveBannerEntry(); + } TileElementRemove(&first[index]); MapInvalidateTileFull(_coords); } diff --git a/src/openrct2/scripting/bindings/world/ScTileElement.cpp b/src/openrct2/scripting/bindings/world/ScTileElement.cpp index 09f73229b8f3..a6b1b5d7f4e8 100644 --- a/src/openrct2/scripting/bindings/world/ScTileElement.cpp +++ b/src/openrct2/scripting/bindings/world/ScTileElement.cpp @@ -15,6 +15,8 @@ # include "../../../common.h" # include "../../../core/Guard.hpp" # include "../../../entity/EntityRegistry.h" +# include "../../../object/LargeSceneryEntry.h" +# include "../../../object/WallSceneryEntry.h" # include "../../../ride/Ride.h" # include "../../../ride/RideData.h" # include "../../../ride/Track.h" @@ -63,6 +65,7 @@ namespace OpenRCT2::Scripting void ScTileElement::type_set(std::string value) { + RemoveBannerEntryIfNeeded(); if (value == "surface") _element->SetType(TileElementType::Surface); else if (value == "footpath") @@ -85,7 +88,7 @@ namespace OpenRCT2::Scripting scriptEngine.LogPluginInfo("Element type not recognised!"); return; } - + CreateBannerEntryIfNeeded(); Invalidate(); } @@ -537,8 +540,10 @@ namespace OpenRCT2::Scripting { case TileElementType::LargeScenery: { + RemoveBannerEntryIfNeeded(); auto* el = _element->AsLargeScenery(); el->SetSequenceIndex(value.as_uint()); + CreateBannerEntryIfNeeded(); Invalidate(); break; } @@ -1194,15 +1199,19 @@ namespace OpenRCT2::Scripting } case TileElementType::LargeScenery: { + RemoveBannerEntryIfNeeded(); auto* el = _element->AsLargeScenery(); el->SetEntryIndex(index); + CreateBannerEntryIfNeeded(); Invalidate(); break; } case TileElementType::Wall: { + RemoveBannerEntryIfNeeded(); auto* el = _element->AsWall(); el->SetEntryIndex(index); + CreateBannerEntryIfNeeded(); Invalidate(); break; } @@ -2140,6 +2149,129 @@ namespace OpenRCT2::Scripting MapInvalidateTileFull(_coords); } + const LargeSceneryElement* ScTileElement::GetOtherLargeSceneryElement( + const CoordsXY& loc, const LargeSceneryElement* const largeScenery) + { + const auto* const largeEntry = largeScenery->GetEntry(); + const auto direction = largeScenery->GetDirection(); + const auto sequenceIndex = largeScenery->GetSequenceIndex(); + const auto* tiles = largeEntry->tiles; + const auto& tile = tiles[sequenceIndex]; + const auto rotatedFirstTile = CoordsXYZ{ + CoordsXY{ tile.x_offset, tile.y_offset }.Rotate(direction), + tile.z_offset, + }; + + const auto firstTile = CoordsXYZ{ loc, largeScenery->GetBaseZ() } - rotatedFirstTile; + for (int32_t i = 0; tiles[i].x_offset != -1; i++) + { + const auto rotatedCurrentTile = CoordsXYZ{ CoordsXY{ tiles[i].x_offset, tiles[i].y_offset }.Rotate(direction), + tiles[i].z_offset }; + + const auto currentTile = firstTile + rotatedCurrentTile; + + const TileElement* tileElement = MapGetFirstElementAt(currentTile); + if (tileElement != nullptr) + { + do + { + if (tileElement->GetType() != TileElementType::LargeScenery) + continue; + if (tileElement->GetDirection() != direction) + continue; + if (tileElement->GetBaseZ() != currentTile.z) + continue; + + if (tileElement->AsLargeScenery() == largeScenery) + continue; + if (tileElement->AsLargeScenery()->GetEntryIndex() != largeScenery->GetEntryIndex()) + continue; + if (tileElement->AsLargeScenery()->GetSequenceIndex() != i) + continue; + + return tileElement->AsLargeScenery(); + } while (!(tileElement++)->IsLastForTile()); + } + } + return nullptr; + } + + void ScTileElement::RemoveBannerEntryIfNeeded() + { + // check if other element still uses the banner entry + if (_element->GetType() == TileElementType::LargeScenery + && _element->AsLargeScenery()->GetEntry()->scrolling_mode != SCROLLING_MODE_NONE + && GetOtherLargeSceneryElement(_coords, _element->AsLargeScenery()) != nullptr) + return; + // remove banner entry (if one exists) + _element->RemoveBannerEntry(); + } + + void ScTileElement::CreateBannerEntryIfNeeded() + { + // check if creation is needed + switch (_element->GetType()) + { + case TileElementType::Banner: + break; + case TileElementType::Wall: + { + auto wallEntry = _element->AsWall()->GetEntry(); + if (wallEntry->scrolling_mode == SCROLLING_MODE_NONE) + return; + break; + } + case TileElementType::LargeScenery: + { + auto largeScenery = _element->AsLargeScenery(); + auto largeSceneryEntry = largeScenery->GetEntry(); + if (largeSceneryEntry == nullptr || largeSceneryEntry->scrolling_mode == SCROLLING_MODE_NONE) + return; + + auto otherElement = GetOtherLargeSceneryElement(_coords, largeScenery); + if (otherElement != nullptr) + { + largeScenery->SetBannerIndex(otherElement->GetBannerIndex()); + return; + } + + break; + } + default: + return; + } + + // create banner entry and initialise it + auto* banner = CreateBanner(); + if (banner == nullptr) + GetContext()->GetScriptEngine().LogPluginInfo("No free banners available."); + else + { + banner->text = {}; + banner->colour = 0; + banner->text_colour = 0; + banner->flags = 0; + if (_element->GetType() == TileElementType::Wall) + banner->flags = BANNER_FLAG_IS_WALL; + if (_element->GetType() == TileElementType::LargeScenery) + banner->flags = BANNER_FLAG_IS_LARGE_SCENERY; + banner->type = 0; + banner->position = TileCoordsXY(_coords); + + if (_element->GetType() == TileElementType::Wall || _element->GetType() == TileElementType::LargeScenery) + { + RideId rideIndex = BannerGetClosestRideIndex({ _coords, _element->BaseHeight }); + if (!rideIndex.IsNull()) + { + banner->ride_index = rideIndex; + banner->flags |= BANNER_FLAG_LINKED_TO_RIDE; + } + } + + _element->SetBannerIndex(banner->id); + } + } + void ScTileElement::Register(duk_context* ctx) { // All diff --git a/src/openrct2/scripting/bindings/world/ScTileElement.hpp b/src/openrct2/scripting/bindings/world/ScTileElement.hpp index 1d039d8325e8..a5bcddc084c9 100644 --- a/src/openrct2/scripting/bindings/world/ScTileElement.hpp +++ b/src/openrct2/scripting/bindings/world/ScTileElement.hpp @@ -210,7 +210,12 @@ namespace OpenRCT2::Scripting void Invalidate(); + void RemoveBannerEntryIfNeeded(); + void CreateBannerEntryIfNeeded(); + public: + static const LargeSceneryElement* GetOtherLargeSceneryElement( + const CoordsXY& loc, const LargeSceneryElement* const largeScenery); static void Register(duk_context* ctx); };