From 29503e660631f46b2aa783c2be860ec3ef43a20c Mon Sep 17 00:00:00 2001 From: X123M3-256 Date: Fri, 17 Jan 2025 18:38:15 +0000 Subject: [PATCH] Allow cable lift to start after block brake --- data/language/en-GB.txt | 2 +- src/openrct2/localisation/StringIds.h | 2 +- src/openrct2/network/NetworkBase.cpp | 2 +- src/openrct2/ride/CableLift.cpp | 5 +- src/openrct2/ride/Ride.cpp | 144 +++++++++++--------------- src/openrct2/ride/Vehicle.cpp | 99 +++++++++--------- src/openrct2/ride/Vehicle.h | 1 + 7 files changed, 119 insertions(+), 136 deletions(-) diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 5ad819fc9c..909181fc5a 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -2169,7 +2169,6 @@ STR_3136 :Warning: This design will be built with an alternative vehicle type STR_3137 :Select Nearby Scenery STR_3138 :Reset Selection STR_3139 :Cable lift unable to work in this operating mode -STR_3140 :Cable lift hill must start immediately after station STR_3141 :Multi-circuit per ride not possible with cable lift hill STR_3142 :{WINDOW_COLOUR_2}Capacity: {BLACK}{STRINGID} STR_3143 :Show people on map @@ -3792,3 +3791,4 @@ STR_6725 :X: STR_6726 :Y: STR_6727 :Dive Loop (left) STR_6728 :Dive Loop (right) +STR_6729 :Cable lift hill must start immediately after station or block brake diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index d2f48602e5..eca57ead66 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -1187,7 +1187,7 @@ enum : StringId STR_CANT_REPAINT_THIS = 3103, STR_ERR_UNABLE_TO_BUILD_THIS_ON_SLOPE = 3133, STR_CABLE_LIFT_UNABLE_TO_WORK_IN_THIS_OPERATING_MODE = 3139, - STR_CABLE_LIFT_HILL_MUST_START_IMMEDIATELY_AFTER_STATION = 3140, + STR_CABLE_LIFT_HILL_MUST_START_IMMEDIATELY_AFTER_STATION_OR_BLOCK_BRAKE = 6729, STR_MULTICIRCUIT_NOT_POSSIBLE_WITH_CABLE_LIFT_HILL = 3141, STR_SELECT_NUMBER_OF_CIRCUITS_TIP = 3160, diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 7de365c43f..0194e5ab8c 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -50,7 +50,7 @@ using namespace OpenRCT2; // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -constexpr uint8_t kNetworkStreamVersion = 3; +constexpr uint8_t kNetworkStreamVersion = 4; const std::string kNetworkStreamID = std::string(OPENRCT2_VERSION) + "-" + std::to_string(kNetworkStreamVersion); diff --git a/src/openrct2/ride/CableLift.cpp b/src/openrct2/ride/CableLift.cpp index 7b410a0185..00613a67cd 100644 --- a/src/openrct2/ride/CableLift.cpp +++ b/src/openrct2/ride/CableLift.cpp @@ -327,7 +327,10 @@ bool Vehicle::CableLiftUpdateTrackMotionBackwards() SetTrackDirection(output.begin_direction); SetTrackType(output.begin_element->AsTrack()->GetTrackType()); - if (output.begin_element->AsTrack()->GetTrackType() == TrackElemType::EndStation) + // Doesn't check for diagonal block brakes because there is no diagonal cable lift piece, + // no way for a cable lift to start from a diagonal brake. + if (output.begin_element->AsTrack()->GetTrackType() == TrackElemType::EndStation + || output.begin_element->AsTrack()->GetTrackType() == TrackElemType::BlockBrakes) { _vehicleMotionTrackFlags = VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_STATION; } diff --git a/src/openrct2/ride/Ride.cpp b/src/openrct2/ride/Ride.cpp index 77b41badd0..a376e5443f 100644 --- a/src/openrct2/ride/Ride.cpp +++ b/src/openrct2/ride/Ride.cpp @@ -3561,7 +3561,6 @@ static void RideCreateVehiclesFindFirstBlock(const Ride& ride, CoordsXYE* outXYE } [[fallthrough]]; case TrackElemType::EndStation: - case TrackElemType::CableLiftHill: case TrackElemType::BlockBrakes: *outXYElement = { trackPos, reinterpret_cast(trackElement) }; return; @@ -3681,12 +3680,8 @@ void Ride::MoveTrainsToBlockBrakes(const CoordsXYZ& firstBlockPosition, TrackEle cableLiftTileElement = MapGetTrackElementAt(CableLiftLoc); if (cableLiftTileElement != nullptr) { - cableLiftTileElement = MapGetTrackElementAt(CableLiftLoc); - if (cableLiftTileElement != nullptr) - { - CoordsXYZ location = CableLiftLoc; - cableLiftPreviousBlock = TrackGetPreviousBlock(location, reinterpret_cast(cableLiftTileElement)); - } + CoordsXYZ location = CableLiftLoc; + cableLiftPreviousBlock = TrackGetPreviousBlock(location, reinterpret_cast(cableLiftTileElement)); } } @@ -3762,6 +3757,24 @@ void Ride::MoveTrainsToBlockBrakes(const CoordsXYZ& firstBlockPosition, TrackEle } } +static bool RideGetStationTile(const Ride& ride, CoordsXYE* output) +{ + for (const auto& station : ride.GetStations()) + { + CoordsXYZ trackStart = station.GetStart(); + if (trackStart.IsNull()) + continue; + + TileElement* tileElement = MapGetTrackElementAtOfType(trackStart, TrackElemType::EndStation); + if (tileElement == nullptr) + continue; + + *output = { trackStart.x, trackStart.y, tileElement }; + return true; + } + return false; +} + /** * Checks and initialises the cable lift track returns false if unable to find * appropriate track. @@ -3769,100 +3782,65 @@ void Ride::MoveTrainsToBlockBrakes(const CoordsXYZ& firstBlockPosition, TrackEle */ static ResultWithMessage RideInitialiseCableLiftTrack(const Ride& ride, bool isApplying) { - CoordsXYZ location; - location.SetNull(); - for (const auto& station : ride.GetStations()) - { - location = station.GetStart(); - if (!location.IsNull()) - break; - } + // Despawn existing cable lift tiles + CoordsXYE stationTile; + if (!RideGetStationTile(ride, &stationTile)) + return { false, STR_CABLE_LIFT_HILL_MUST_START_IMMEDIATELY_AFTER_STATION_OR_BLOCK_BRAKE }; - if (location.IsNull()) + if (isApplying) { - return { false, STR_CABLE_LIFT_HILL_MUST_START_IMMEDIATELY_AFTER_STATION }; - } + // In case circuit is incomplete, find the start of the track in order to ensure all tiles connected + // to the station are cleared + RideGetStartOfTrack(&stationTile); - bool success = false; - TileElement* tileElement = MapGetFirstElementAt(location); - if (tileElement == nullptr) - return { false }; - do - { - if (tileElement->GetType() != TileElementType::Track) - continue; - if (tileElement->GetBaseZ() != location.z) - continue; - - const auto& ted = GetTrackElementDescriptor(tileElement->AsTrack()->GetTrackType()); - if (!(ted.sequences[0].flags & TRACK_SEQUENCE_FLAG_ORIGIN)) + TrackCircuitIterator it; + TrackCircuitIteratorBegin(&it, stationTile); + while (TrackCircuitIteratorNext(&it)) { - continue; + TileElement* tileElement = it.current.element; + GetTrackElementOriginAndApplyChanges( + { { it.current, tileElement->GetBaseZ() }, tileElement->GetDirection() }, + tileElement->AsTrack()->GetTrackType(), 0, &tileElement, TRACK_ELEMENT_SET_HAS_CABLE_LIFT_FALSE); } - success = true; - break; - } while (!(tileElement++)->IsLastForTile()); + } - if (!success) + // Spawn new cable lift tiles + auto cableLiftTileElement = MapGetTrackElementAt(ride.CableLiftLoc); + CoordsXYE cableLiftCoords = { ride.CableLiftLoc, reinterpret_cast(cableLiftTileElement) }; + if (cableLiftTileElement == nullptr) return { false }; - enum - { - STATE_FIND_CABLE_LIFT, - STATE_FIND_STATION, - STATE_REST_OF_TRACK - }; - int32_t state = STATE_FIND_CABLE_LIFT; - TrackCircuitIterator it; - TrackCircuitIteratorBegin(&it, { location, tileElement }); + TrackCircuitIteratorBegin(&it, cableLiftCoords); while (TrackCircuitIteratorPrevious(&it)) { - tileElement = it.current.element; + TileElement* tileElement = it.current.element; auto trackType = tileElement->AsTrack()->GetTrackType(); - - uint16_t flags = TRACK_ELEMENT_SET_HAS_CABLE_LIFT_FALSE; - switch (state) + switch (trackType) { - case STATE_FIND_CABLE_LIFT: - // Search for a cable lift hill track element - if (trackType == TrackElemType::CableLiftHill) + case TrackElemType::Up25: + case TrackElemType::Up60: + case TrackElemType::FlatToUp25: + case TrackElemType::Up25ToFlat: + case TrackElemType::Up25ToUp60: + case TrackElemType::Up60ToUp25: + case TrackElemType::FlatToUp60LongBase: + case TrackElemType::Flat: + if (isApplying) { - flags = TRACK_ELEMENT_SET_HAS_CABLE_LIFT_TRUE; - state = STATE_FIND_STATION; + GetTrackElementOriginAndApplyChanges( + { { it.current, tileElement->GetBaseZ() }, tileElement->GetDirection() }, trackType, 0, &tileElement, + TRACK_ELEMENT_SET_HAS_CABLE_LIFT_TRUE); } break; - case STATE_FIND_STATION: - // Search for the start of the hill - switch (trackType) - { - case TrackElemType::Flat: - case TrackElemType::Up25: - case TrackElemType::Up60: - case TrackElemType::FlatToUp25: - case TrackElemType::Up25ToFlat: - case TrackElemType::Up25ToUp60: - case TrackElemType::Up60ToUp25: - case TrackElemType::FlatToUp60LongBase: - flags = TRACK_ELEMENT_SET_HAS_CABLE_LIFT_TRUE; - break; - case TrackElemType::EndStation: - state = STATE_REST_OF_TRACK; - break; - default: - return { false, STR_CABLE_LIFT_HILL_MUST_START_IMMEDIATELY_AFTER_STATION }; - } - break; - } - if (isApplying) - { - auto tmpLoc = CoordsXYZ{ it.current, tileElement->GetBaseZ() }; - auto direction = tileElement->GetDirection(); - trackType = tileElement->AsTrack()->GetTrackType(); - GetTrackElementOriginAndApplyChanges({ tmpLoc, direction }, trackType, 0, &tileElement, flags); + case TrackElemType::EndStation: + case TrackElemType::BlockBrakes: + return { true }; + default: + return { false, STR_CABLE_LIFT_HILL_MUST_START_IMMEDIATELY_AFTER_STATION_OR_BLOCK_BRAKE }; } } - return { true }; + return { false, STR_CABLE_LIFT_HILL_MUST_START_IMMEDIATELY_AFTER_STATION_OR_BLOCK_BRAKE }; } /** diff --git a/src/openrct2/ride/Vehicle.cpp b/src/openrct2/ride/Vehicle.cpp index be6de68fec..d8c4b290f2 100644 --- a/src/openrct2/ride/Vehicle.cpp +++ b/src/openrct2/ride/Vehicle.cpp @@ -5485,6 +5485,36 @@ void Vehicle::ApplyStopBlockBrake() } } +void Vehicle::ApplyCableLiftBlockBrake(bool brakeClosed) +{ + // If we are already on the cable lift, ignore the brake + if (status == Vehicle::Status::TravellingCableLift) + return; + + // Slow down if travelling faster than 4mph + if (velocity > kBlockBrakeBaseSpeed) + { + velocity -= velocity >> 3; + acceleration = 0; + } + // Once moving slow enough, keep moving forward until lined up with the catch car + else if (track_progress <= 18) + { + velocity = kBlockBrakeBaseSpeed; + acceleration = 0; + } + // Once in position, stop, and wait for the catch car + if (velocity > 0 && track_progress >= 18) + { + velocity = 0; + acceleration = 0; + if (!brakeClosed) + SetState(Vehicle::Status::WaitingForCableLift, sub_state); + else + _vehicleMotionTrackFlags |= VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_BLOCK_BRAKE; + } +} + /** * * rct2: 0x006DAC43 @@ -5518,6 +5548,20 @@ void Vehicle::CheckAndApplyBlockSectionStopSite() switch (trackType) { case TrackElemType::BlockBrakes: + // Check if this brake is the start of a cable lift + if (curRide->lifecycle_flags & RIDE_LIFECYCLE_CABLE_LIFT) + { + CoordsXYE track; + int32_t zUnused; + int32_t direction; + if (TrackBlockGetNextFromZero(TrackLocation, *curRide, GetTrackDirection(), &track, &zUnused, &direction, false) + && track.element != nullptr && track.element->AsTrack()->HasCableLift()) + { + ApplyCableLiftBlockBrake(curRide->IsBlockSectioned() && trackElement->AsTrack()->IsBrakeClosed()); + break; + } + } + [[fallthrough]]; case TrackElemType::DiagBlockBrakes: if (curRide->IsBlockSectioned() && trackElement->AsTrack()->IsBrakeClosed()) ApplyStopBlockBrake(); @@ -5577,56 +5621,13 @@ void Vehicle::UpdateVelocity() _vehicleVelocityF64E0C = (nextVelocity >> 10) * 42; } -static void block_brakes_open_previous_section( - const Ride& ride, const CoordsXYZ& vehicleTrackLocation, TileElement* tileElement) +static void BlockBrakesOpenPreviousSection(const Ride& ride, const CoordsXYZ& vehicleTrackLocation, TileElement* tileElement) { - auto location = vehicleTrackLocation; - TrackBeginEnd trackBeginEnd, slowTrackBeginEnd; - TileElement slowTileElement = *tileElement; - bool counter = true; - CoordsXY slowLocation = location; - do - { - if (!TrackBlockGetPrevious({ location, tileElement }, &trackBeginEnd)) - { - return; - } - if (trackBeginEnd.begin_x == vehicleTrackLocation.x && trackBeginEnd.begin_y == vehicleTrackLocation.y - && tileElement == trackBeginEnd.begin_element) - { - return; - } - - location.x = trackBeginEnd.end_x; - location.y = trackBeginEnd.end_y; - location.z = trackBeginEnd.begin_z; - tileElement = trackBeginEnd.begin_element; - - // #2081: prevent infinite loop - counter = !counter; - if (counter) - { - TrackBlockGetPrevious({ slowLocation, &slowTileElement }, &slowTrackBeginEnd); - slowLocation.x = slowTrackBeginEnd.end_x; - slowLocation.y = slowTrackBeginEnd.end_y; - slowTileElement = *(slowTrackBeginEnd.begin_element); - if (slowLocation == location && slowTileElement.GetBaseZ() == tileElement->GetBaseZ() - && slowTileElement.GetType() == tileElement->GetType() - && slowTileElement.GetDirection() == tileElement->GetDirection()) - { - return; - } - } - } while (!(trackBeginEnd.begin_element->AsTrack()->IsBlockStart())); - - // Get the start of the track block instead of the end - location = { trackBeginEnd.begin_x, trackBeginEnd.begin_y, trackBeginEnd.begin_z }; - auto trackOrigin = MapGetTrackElementAtOfTypeSeq(location, trackBeginEnd.begin_element->AsTrack()->GetTrackType(), 0); - if (trackOrigin == nullptr) - { + CoordsXYZ location = vehicleTrackLocation; + TrackElement* trackElement = TrackGetPreviousBlock(location, tileElement); + if (trackElement == nullptr) return; - } - auto trackElement = trackOrigin->AsTrack(); + SetBrakeClosedMultiTile(*trackElement, location, false); MapInvalidateElement(location, reinterpret_cast(trackElement)); @@ -6921,7 +6922,7 @@ bool Vehicle::UpdateTrackMotionForwardsGetNewTrack( } } MapInvalidateElement(TrackLocation, tileElement); - block_brakes_open_previous_section(curRide, TrackLocation, tileElement); + BlockBrakesOpenPreviousSection(curRide, TrackLocation, tileElement); if (TrackTypeIsBlockBrakes(trackType)) { BlockBrakeSetLinkedBrakesClosed(TrackLocation, *tileElement->AsTrack(), true); diff --git a/src/openrct2/ride/Vehicle.h b/src/openrct2/ride/Vehicle.h index e455fe763b..c5c03a82ef 100644 --- a/src/openrct2/ride/Vehicle.h +++ b/src/openrct2/ride/Vehicle.h @@ -347,6 +347,7 @@ private: void UpdateTrackMotionUpStopCheck() const; void ApplyNonStopBlockBrake(); void ApplyStopBlockBrake(); + void ApplyCableLiftBlockBrake(bool brakeClosed); void CheckAndApplyBlockSectionStopSite(); void UpdateVelocity(); void UpdateSpinningCar();