From 1f7ef646279516a83112378aee65699410be06a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Tue, 28 Mar 2023 00:16:55 +0200 Subject: [PATCH] Fix #19574: Handle exits in null locations (#19727) In park/replay from dump #19574 there was a calamity that happened likely due to a plugin. Pretty much all rides were deleted in single tick, which caused the park to mark some locations as null and then it tripped on one of the many assertions. In many of the places it already bails out if ride or vehicle is a nullptr, so I followed with similar approach for invalid ride exits. --- distribution/changelog.txt | 1 + src/openrct2/entity/Guest.cpp | 67 ++++++++++++++++++++++++++------- src/openrct2/world/Footpath.cpp | 12 +++--- src/openrct2/world/Footpath.h | 6 +-- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 89b3afa7c0..aa1f740f76 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -68,6 +68,7 @@ - Fix: [#19493] SV4 saves not importing the correct vehicle colours. - Fix: [#19517] Crash when peeps try to exit or enter hacked rides that have no waypoints specified. - Fix: [#19524] Staff counter shows incorrect values if there are more than 32767 staff members. +- Fix: [#19574] Handle exits in null locations. - Fix: [#19641, #19643] Missing water tile in Infernal Views’ and Six Flags Holland’s river. 0.4.3 (2022-12-14) diff --git a/src/openrct2/entity/Guest.cpp b/src/openrct2/entity/Guest.cpp index 14e45644e6..04a82658e8 100644 --- a/src/openrct2/entity/Guest.cpp +++ b/src/openrct2/entity/Guest.cpp @@ -3597,7 +3597,10 @@ uint8_t Guest::GetWaypointedSeatLocation(const Ride& ride, const CarEntry* vehic void Guest::UpdateRideLeaveEntranceWaypoints(const Ride& ride) { const auto& station = ride.GetStation(CurrentRideStation); - Guard::Assert(!station.Entrance.IsNull()); + if (station.Entrance.IsNull()) + { + return; + } uint8_t direction_entrance = station.Entrance.direction; TileElement* tile_element = RideGetStationStartTrackElement(ride, CurrentRideStation); @@ -3684,7 +3687,10 @@ void Guest::UpdateRideAdvanceThroughEntrance() { const auto& station = ride->GetStation(CurrentRideStation); auto entranceLocation = station.Entrance.ToCoordsXYZD(); - Guard::Assert(!entranceLocation.IsNull()); + if (entranceLocation.IsNull()) + { + return; + } const auto& rtd = GetRideTypeDescriptor(ride->type); rtd.UpdateLeaveEntrance(this, *ride, entranceLocation); @@ -3770,7 +3776,6 @@ static void PeepGoToRideExit(Peep* peep, const Ride& ride, int16_t x, int16_t y, Guard::Assert(peep->CurrentRideStation.ToUnderlying() < OpenRCT2::Limits::MaxStationsPerRide); auto exit = ride.GetStation(peep->CurrentRideStation).Exit; - Guard::Assert(!exit.IsNull()); x = exit.x; y = exit.y; x *= 32; @@ -3778,8 +3783,16 @@ static void PeepGoToRideExit(Peep* peep, const Ride& ride, int16_t x, int16_t y, x += 16; y += 16; - int16_t x_shift = DirectionOffsets[exit_direction].x; - int16_t y_shift = DirectionOffsets[exit_direction].y; + auto [x_shift, y_shift] = [exit_direction]() { + if (exit_direction < DirectionOffsets.size()) + { + return std::pair(DirectionOffsets[exit_direction].x, DirectionOffsets[exit_direction].y); + } + else + { + return std::pair(0, 0); + } + }(); int16_t shift_multiplier = 20; @@ -3877,8 +3890,11 @@ static void PeepUpdateRideNoFreeVehicleRejoinQueue(Guest* peep, Ride& ride) int32_t x = entranceLocation.x * 32; int32_t y = entranceLocation.y * 32; - x += 16 - DirectionOffsets[entranceLocation.direction].x * 20; - y += 16 - DirectionOffsets[entranceLocation.direction].y * 20; + if (entranceLocation.direction < DirectionOffsets.size()) + { + x += 16 - DirectionOffsets[entranceLocation.direction].x * 20; + y += 16 - DirectionOffsets[entranceLocation.direction].y * 20; + } peep->SetDestination({ x, y }, 2); peep->SetState(PeepState::QueuingFront); @@ -4188,8 +4204,16 @@ void Guest::UpdateRideLeaveVehicle() } } - int16_t xShift = DirectionOffsets[specialDirection].x; - int16_t yShift = DirectionOffsets[specialDirection].y; + auto [xShift, yShift] = [specialDirection]() { + if (specialDirection < DirectionOffsets.size()) + { + return std::pair(DirectionOffsets[specialDirection].x, DirectionOffsets[specialDirection].y); + } + else + { + return std::pair(0, 0); + } + }(); platformLocation.x = vehicle->x + xShift * shiftMultiplier; platformLocation.y = vehicle->y + yShift * shiftMultiplier; @@ -4237,7 +4261,10 @@ void Guest::UpdateRideLeaveVehicle() } auto exitLocation = station.Exit.ToCoordsXYZD(); - Guard::Assert(!exitLocation.IsNull()); + if (exitLocation.IsNull()) + { + return; + } TileElement* trackElement = RideGetStationStartTrackElement(*ride, CurrentRideStation); @@ -4297,8 +4324,17 @@ void Guest::UpdateRidePrepareForExit() auto exit = ride->GetStation(CurrentRideStation).Exit; auto newDestination = exit.ToCoordsXY().ToTileCentre(); - auto xShift = DirectionOffsets[exit.direction].x; - auto yShift = DirectionOffsets[exit.direction].y; + + auto [xShift, yShift] = [exit]() { + if (exit.direction < DirectionOffsets.size()) + { + return std::pair(DirectionOffsets[exit.direction].x, DirectionOffsets[exit.direction].y); + } + else + { + return std::pair(0, 0); + } + }(); int16_t shiftMultiplier = 20; @@ -4618,7 +4654,12 @@ void Guest::UpdateRideApproachSpiralSlide() { auto exit = ride->GetStation(CurrentRideStation).Exit; waypoint = 1; - Var37 = (exit.direction * 4) | (Var37 & 0x30) | waypoint; + auto directionTemp = exit.direction; + if (exit.direction == INVALID_DIRECTION) + { + directionTemp = 0; + } + Var37 = (directionTemp * 4) | (Var37 & 0x30) | waypoint; CoordsXY targetLoc = ride->GetStation(CurrentRideStation).Start; assert(rtd.HasFlag(RIDE_TYPE_FLAG_IS_SPIRAL_SLIDE)); diff --git a/src/openrct2/world/Footpath.cpp b/src/openrct2/world/Footpath.cpp index b3c8b372eb..a38966c0d9 100644 --- a/src/openrct2/world/Footpath.cpp +++ b/src/openrct2/world/Footpath.cpp @@ -61,8 +61,8 @@ static RideId _footpathQueueChain[64]; // This is the coordinates that a user of the bin should move to // rct2: 0x00992A4C -const CoordsXY BinUseOffsets[4] = { - { 11, 16 }, +const std::array BinUseOffsets = { + CoordsXY{ 11, 16 }, { 16, 21 }, { 21, 16 }, { 16, 11 }, @@ -70,13 +70,13 @@ const CoordsXY BinUseOffsets[4] = { // These are the offsets for bench positions on footpaths, 2 for each edge // rct2: 0x00981F2C, 0x00981F2E -const CoordsXY BenchUseOffsets[8] = { - { 7, 12 }, { 12, 25 }, { 25, 20 }, { 20, 7 }, { 7, 20 }, { 20, 25 }, { 25, 12 }, { 12, 7 }, +const std::array BenchUseOffsets = { + CoordsXY{ 7, 12 }, { 12, 25 }, { 25, 20 }, { 20, 7 }, { 7, 20 }, { 20, 25 }, { 25, 12 }, { 12, 7 }, }; /** rct2: 0x00981D6C, 0x00981D6E */ -const CoordsXY DirectionOffsets[4] = { - { -1, 0 }, +const std::array DirectionOffsets = { + CoordsXY{ -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, diff --git a/src/openrct2/world/Footpath.h b/src/openrct2/world/Footpath.h index 3af84db0c9..03d935d364 100644 --- a/src/openrct2/world/Footpath.h +++ b/src/openrct2/world/Footpath.h @@ -180,9 +180,9 @@ extern uint8_t gFootpathConstructSlope; extern uint8_t gFootpathGroundFlags; // Given a direction, this will return how to increase/decrease the x and y coordinates. -extern const CoordsXY DirectionOffsets[NumOrthogonalDirections]; -extern const CoordsXY BinUseOffsets[NumOrthogonalDirections]; -extern const CoordsXY BenchUseOffsets[NumOrthogonalDirections * 2]; +extern const std::array DirectionOffsets; +extern const std::array BinUseOffsets; +extern const std::array BenchUseOffsets; TileElement* MapGetFootpathElement(const CoordsXYZ& coords); void FootpathInterruptPeeps(const CoordsXYZ& footpathPos);