diff --git a/CMakeLists.txt b/CMakeLists.txt index cb9253ef5a..dc3fad1b08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,9 +76,9 @@ set(OPENMSX_VERSION "1.1.0") set(OPENMSX_URL "https://github.com/OpenRCT2/OpenMusic/releases/download/v${OPENMSX_VERSION}/openmusic.zip") set(OPENMSX_SHA1 "8f0cf6b2fd4727e91b0d4062e7f199a43d15e777") -set(REPLAYS_VERSION "0.0.75") +set(REPLAYS_VERSION "0.0.76") set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v${REPLAYS_VERSION}/replays.zip") -set(REPLAYS_SHA1 "D1701450AE0FE84B144236243A925801B67D92ED") +set(REPLAYS_SHA1 "AE5808DE726D27F5311731677A20C96A8FF9101F") option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.") option(WITH_TESTS "Build tests") diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 343512f178..cb62155f52 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -30,6 +30,7 @@ - Improved: [#19463] Added ‘W’ and ‘Y’ with circumflex to sprite font (for Welsh). - Improved: [#19549] Enable large address awareness for 32 bit Windows builds allowing to use 4 GiB of virtual memory. - Improved: [#19668] Decreased the minimum map size from 13 to 3. +- Improved: [#19683] The delays for ride ratings to appear has been reduced drastically. - Change: [#19018] Renamed actions to fit the naming scheme. - Change: [#19091] [Plugin] Add game action information to callback arguments of custom actions. - Change: [#19233] Reduce lift speed minimum and maximum values for “Classic Wooden Coaster”. diff --git a/openrct2.proj b/openrct2.proj index b3202b4ded..b6515e9a5c 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -51,8 +51,8 @@ 64EF7E0B7785602C91AEC66F005C035B05A2133B https://github.com/OpenRCT2/OpenMusic/releases/download/v1.1.0/openmusic.zip 8f0cf6b2fd4727e91b0d4062e7f199a43d15e777 - https://github.com/OpenRCT2/replays/releases/download/v0.0.75/replays.zip - D1701450AE0FE84B144236243A925801B67D92ED + https://github.com/OpenRCT2/replays/releases/download/v0.0.76/replays.zip + AE5808DE726D27F5311731677A20C96A8FF9101F diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 35a13838b1..3bf494f91b 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -43,7 +43,7 @@ // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "12" +#define NETWORK_STREAM_VERSION "13" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION diff --git a/src/openrct2/park/ParkFile.cpp b/src/openrct2/park/ParkFile.cpp index a30fe38b1a..53d6421f68 100644 --- a/src/openrct2/park/ParkFile.cpp +++ b/src/openrct2/park/ParkFile.cpp @@ -536,7 +536,25 @@ namespace OpenRCT2 cs.ReadWrite(gGrassSceneryTileLoopPosition); cs.ReadWrite(gWidePathTileLoopPosition); - ReadWriteRideRatingCalculationData(cs, gRideRatingUpdateState); + auto& rideRatings = RideRatingGetUpdateStates(); + if (os.GetHeader().TargetVersion >= 21) + { + cs.ReadWriteArray(rideRatings, [this, &cs](RideRatingUpdateState& calcData) { + ReadWriteRideRatingCalculationData(cs, calcData); + return true; + }); + } + else + { + // Only single state was stored prior to version 20. + if (os.GetMode() == OrcaStream::Mode::READING) + { + // Since we read only one state ensure the rest is reset. + RideRatingResetUpdateStates(); + } + auto& rideRatingUpdateState = rideRatings[0]; + ReadWriteRideRatingCalculationData(cs, rideRatingUpdateState); + } if (os.GetHeader().TargetVersion >= 14) { diff --git a/src/openrct2/park/ParkFile.h b/src/openrct2/park/ParkFile.h index 4c7e1bc042..69fe23f2b5 100644 --- a/src/openrct2/park/ParkFile.h +++ b/src/openrct2/park/ParkFile.h @@ -9,10 +9,10 @@ struct ObjectRepositoryItem; namespace OpenRCT2 { // Current version that is saved. - constexpr uint32_t PARK_FILE_CURRENT_VERSION = 20; + constexpr uint32_t PARK_FILE_CURRENT_VERSION = 21; // The minimum version that is forwards compatible with the current version. - constexpr uint32_t PARK_FILE_MIN_VERSION = 19; + constexpr uint32_t PARK_FILE_MIN_VERSION = 21; // The minimum version that is backwards compatible with the current version. // If this is increased beyond 0, uncomment the checks in ParkFile.cpp and Context.cpp! diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 76fe31644a..9ee12d3c7c 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -1097,7 +1097,10 @@ namespace RCT2 void ImportRideRatingsCalcData() { const auto& src = _s6.RideRatingsCalcData; - auto& dst = gRideRatingUpdateState; + // S6 has only one state, ensure we reset all states before reading the first one. + RideRatingResetUpdateStates(); + auto& rideRatingStates = RideRatingGetUpdateStates(); + auto& dst = rideRatingStates[0]; dst = {}; dst.Proximity = { src.ProximityX, src.ProximityY, src.ProximityZ }; dst.ProximityStart = { src.ProximityStartX, src.ProximityStartY, src.ProximityStartZ }; diff --git a/src/openrct2/ride/RideRatings.cpp b/src/openrct2/ride/RideRatings.cpp index c6afa920a3..4ccdeb90f3 100644 --- a/src/openrct2/ride/RideRatings.cpp +++ b/src/openrct2/ride/RideRatings.cpp @@ -77,7 +77,12 @@ struct ShelteredEights uint8_t TotalShelteredEighths; }; -RideRatingUpdateState gRideRatingUpdateState; +static RideRatingUpdateStates gRideRatingUpdateStates; + +// Amount of updates allowed per updating state on the current tick. +// The total amount would be MaxRideRatingSubSteps * RideRatingMaxUpdateStates which +// would be currently 80, this is the worst case of sub-steps and may break out earlier. +static constexpr size_t MaxRideRatingUpdateSubSteps = 20; static void ride_ratings_update_state(RideRatingUpdateState& state); static void ride_ratings_update_state_0(RideRatingUpdateState& state); @@ -90,9 +95,21 @@ static void ride_ratings_begin_proximity_loop(RideRatingUpdateState& state); static void RideRatingsCalculate(RideRatingUpdateState& state, Ride& ride); static void RideRatingsCalculateValue(Ride& ride); static void ride_ratings_score_close_proximity(RideRatingUpdateState& state, TileElement* inputTileElement); - static void ride_ratings_add(RatingTuple* rating, int32_t excitement, int32_t intensity, int32_t nausea); +RideRatingUpdateStates& RideRatingGetUpdateStates() +{ + return gRideRatingUpdateStates; +} + +void RideRatingResetUpdateStates() +{ + RideRatingUpdateState nullState{}; + nullState.State = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + + std::fill(gRideRatingUpdateStates.begin(), gRideRatingUpdateStates.end(), nullState); +} + /** * This is a small hack function to keep calling the ride rating processor until * the given ride's ratings have been calculated. Whatever is currently being @@ -124,9 +141,17 @@ void RideRatingsUpdateAll() if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) return; - // NOTE: With the new save format more than one ride can be updated at once, but this has not yet been implemented. - // The SV6 format could store only a single state. - ride_ratings_update_state(gRideRatingUpdateState); + for (auto& updateState : gRideRatingUpdateStates) + { + for (size_t i = 0; i < MaxRideRatingUpdateSubSteps; ++i) + { + ride_ratings_update_state(updateState); + + // We need to abort the loop if the state machine requested to find the next ride. + if (updateState.State == RIDE_RATINGS_STATE_FIND_NEXT_RIDE) + break; + } + } } static void ride_ratings_update_state(RideRatingUpdateState& state) @@ -154,6 +179,42 @@ static void ride_ratings_update_state(RideRatingUpdateState& state) } } +static bool RideRatingIsUpdatingRide(RideId id) +{ + return std::any_of(gRideRatingUpdateStates.begin(), gRideRatingUpdateStates.end(), [id](auto& state) { + return state.CurrentRide == id && state.State != RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + }); +} + +static bool ShouldSkipRatingCalculation(const Ride& ride) +{ + // Skip anything that isn't a real ride. + if (ride.GetClassification() != RideClassification::Ride) + { + return true; + } + + // Skip rides that are closed. + if (ride.status == RideStatus::Closed) + { + return true; + } + + // Skip anything that is already updating. + if (RideRatingIsUpdatingRide(ride.id)) + { + return true; + } + + // Skip rides that have a fixed rating. + if (ride.lifecycle_flags & RIDE_LIFECYCLE_FIXED_RATINGS) + { + return true; + } + + return false; +} + static RideId GetNextRideToUpdate(RideId currentRide) { auto rm = GetRideManager(); @@ -161,14 +222,31 @@ static RideId GetNextRideToUpdate(RideId currentRide) { return RideId::GetNull(); } - // Skip all empty ride ids - auto nextRide = std::next(rm.get(currentRide)); - // If at end, loop around - if (nextRide == rm.end()) + + auto it = rm.get(currentRide); + if (it == rm.end()) { - nextRide = rm.begin(); + // Start at the beginning, ride is missing. + it = rm.begin(); } - return (*nextRide).id; + else + { + it = std::next(it); + } + + // Filter out rides to avoid wasting a tick to find the next ride. + while (it != rm.end() && ShouldSkipRatingCalculation(*it)) + { + it++; + } + + // If we reached the end of the list we start over, + // in case the next ride doesn't pass the filter function it will + // look for the next matching ride in the next tick. + if (it == rm.end()) + it = rm.begin(); + + return (*it).id; } /** @@ -185,11 +263,11 @@ static void ride_ratings_update_state_0(RideRatingUpdateState& state) state.CurrentRide = {}; } - auto nextRideId = GetNextRideToUpdate(state.CurrentRide); - auto nextRide = GetRide(nextRideId); - if (nextRide != nullptr && nextRide->status != RideStatus::Closed - && !(nextRide->lifecycle_flags & RIDE_LIFECYCLE_FIXED_RATINGS)) + const auto nextRideId = GetNextRideToUpdate(state.CurrentRide); + const auto* nextRide = GetRide(nextRideId); + if (nextRide != nullptr && !ShouldSkipRatingCalculation(*nextRide)) { + Guard::Assert(!RideRatingIsUpdatingRide(nextRideId)); state.State = RIDE_RATINGS_STATE_INITIALISE; } state.CurrentRide = nextRideId; diff --git a/src/openrct2/ride/RideRatings.h b/src/openrct2/ride/RideRatings.h index 7ca932121d..3ed0481744 100644 --- a/src/openrct2/ride/RideRatings.h +++ b/src/openrct2/ride/RideRatings.h @@ -54,7 +54,12 @@ struct RideRatingUpdateState uint16_t StationFlags; }; -extern RideRatingUpdateState gRideRatingUpdateState; +static constexpr size_t RideRatingMaxUpdateStates = 4; + +using RideRatingUpdateStates = std::array; + +RideRatingUpdateStates& RideRatingGetUpdateStates(); +void RideRatingResetUpdateStates(); void RideRatingsUpdateRide(const Ride& ride); void RideRatingsUpdateAll();