diff --git a/src/openrct2/ride/Ride.cpp b/src/openrct2/ride/Ride.cpp index 222f02ab3a..729ff69192 100644 --- a/src/openrct2/ride/Ride.cpp +++ b/src/openrct2/ride/Ride.cpp @@ -3087,9 +3087,15 @@ static Vehicle* vehicle_create_car( vehicle->update_flags = 0; CoordsXY chosenLoc; + auto numAttempts = 0; // loc_6DDD26: do { + numAttempts++; + // This can happen when trying to spawn dozens of cars in a tiny area. + if (numAttempts > 10000) + return nullptr; + vehicle->sprite_direction = scenario_rand() & 0x1E; chosenLoc.y = dodgemPos.y + (scenario_rand() & 0xFF); chosenLoc.x = dodgemPos.x + (scenario_rand() & 0xFF); @@ -3233,15 +3239,16 @@ static train_ref vehicle_create_train( return train; } -static void vehicle_create_trains(ride_id_t rideIndex, const CoordsXYZ& trainsPos, TrackElement* trackElement) +static bool vehicle_create_trains(ride_id_t rideIndex, const CoordsXYZ& trainsPos, TrackElement* trackElement) { auto ride = get_ride(rideIndex); if (ride == nullptr) - return; + return false; train_ref firstTrain = {}; train_ref lastTrain = {}; int32_t remainingDistance = 0; + bool allTrainsCreated = true; for (int32_t vehicleIndex = 0; vehicleIndex < ride->num_vehicles; vehicleIndex++) { @@ -3251,7 +3258,10 @@ static void vehicle_create_trains(ride_id_t rideIndex, const CoordsXYZ& trainsPo } train_ref train = vehicle_create_train(rideIndex, trainsPos, vehicleIndex, &remainingDistance, trackElement); if (train.head == nullptr || train.tail == nullptr) + { + allTrainsCreated = false; continue; + } if (vehicleIndex == 0) { @@ -3280,6 +3290,8 @@ static void vehicle_create_trains(ride_id_t rideIndex, const CoordsXYZ& trainsPo firstTrain.head->prev_vehicle_on_ride = lastTrain.tail->sprite_index; if (firstTrain.head != nullptr) lastTrain.tail->next_vehicle_on_ride = firstTrain.head->sprite_index; + + return allTrainsCreated; } /** @@ -3389,7 +3401,14 @@ bool Ride::CreateVehicles(const CoordsXYE& element, bool isApplying) direction = trackElement->GetDirection(); } - vehicle_create_trains(id, vehiclePos, trackElement); + if (!vehicle_create_trains(id, vehiclePos, trackElement)) + { + // This flag is needed for Ride::RemoveVehicles() + lifecycle_flags |= RIDE_LIFECYCLE_ON_TRACK; + RemoveVehicles(); + gGameCommandErrorText = STR_UNABLE_TO_CREATE_ENOUGH_VEHICLES; + return false; + } // return true; // Initialise station departs diff --git a/src/openrct2/ride/Ride.h b/src/openrct2/ride/Ride.h index 855a42b128..066330478b 100644 --- a/src/openrct2/ride/Ride.h +++ b/src/openrct2/ride/Ride.h @@ -466,6 +466,8 @@ public: uint8_t GetNumShelteredSections() const; void IncreaseNumShelteredSections(); + + void RemoveVehicles(); }; #pragma pack(push, 1) diff --git a/src/openrct2/ride/RideConstruction.cpp b/src/openrct2/ride/RideConstruction.cpp index e2fa615baa..8272d94d78 100644 --- a/src/openrct2/ride/RideConstruction.cpp +++ b/src/openrct2/ride/RideConstruction.cpp @@ -40,6 +40,7 @@ #include "RideData.h" #include "Track.h" #include "TrackData.h" +#include "TrainManager.h" #include "Vehicle.h" bool gGotoStartPlacementMode = false; @@ -173,16 +174,16 @@ static void ride_remove_cable_lift(Ride* ride) * * rct2: 0x006DD506 */ -static void ride_remove_vehicles(Ride* ride) +void Ride::RemoveVehicles() { - if (ride->lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK) + if (lifecycle_flags & RIDE_LIFECYCLE_ON_TRACK) { - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_ON_TRACK; - ride->lifecycle_flags &= ~(RIDE_LIFECYCLE_TEST_IN_PROGRESS | RIDE_LIFECYCLE_HAS_STALLED_VEHICLE); + lifecycle_flags &= ~RIDE_LIFECYCLE_ON_TRACK; + lifecycle_flags &= ~(RIDE_LIFECYCLE_TEST_IN_PROGRESS | RIDE_LIFECYCLE_HAS_STALLED_VEHICLE); for (size_t i = 0; i <= MAX_VEHICLES_PER_RIDE; i++) { - uint16_t spriteIndex = ride->vehicles[i]; + uint16_t spriteIndex = vehicles[i]; while (spriteIndex != SPRITE_INDEX_NULL) { Vehicle* vehicle = GetEntity(spriteIndex); @@ -195,11 +196,21 @@ static void ride_remove_vehicles(Ride* ride) sprite_remove(vehicle); } - ride->vehicles[i] = SPRITE_INDEX_NULL; + vehicles[i] = SPRITE_INDEX_NULL; } for (size_t i = 0; i < MAX_STATIONS; i++) - ride->stations[i].TrainAtStation = RideStation::NO_TRAIN; + stations[i].TrainAtStation = RideStation::NO_TRAIN; + + // Also clean up orphaned vehicles for good measure. + for (auto* vehicle : TrainManager::View()) + { + if (vehicle->ride == id) + { + vehicle->Invalidate(); + sprite_remove(vehicle); + } + } } } @@ -223,7 +234,7 @@ void ride_clear_for_construction(Ride* ride) } ride_remove_cable_lift(ride); - ride_remove_vehicles(ride); + ride->RemoveVehicles(); ride_clear_blocked_tiles(ride); auto w = window_find_by_number(WC_RIDE, ride->id);