Create or delete banners when tile elements are changed by plugins (#21627)

This commit is contained in:
Stephan Spengler 2024-05-17 12:54:51 +02:00 committed by GitHub
parent 919cef7f55
commit f4156e9043
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 149 additions and 3 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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);
};