diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aa6576937..094c7bdcc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,9 +76,9 @@ set(OPENMSX_VERSION "1.2.0") set(OPENMSX_URL "https://github.com/OpenRCT2/OpenMusic/releases/download/v${OPENMSX_VERSION}/openmusic.zip") set(OPENMSX_SHA1 "c1c9b030c83c12ff628e8ec72fcfe748291a5344") -set(REPLAYS_VERSION "0.0.77") +set(REPLAYS_VERSION "0.0.78") set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v${REPLAYS_VERSION}/replays.zip") -set(REPLAYS_SHA1 "65CA6D830789C074F575F15E3F9E2C28691C24F8") +set(REPLAYS_SHA1 "31C5D07EED8481D5C6D57F9E4FE9443AAEDE7739") option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.") option(WITH_TESTS "Build tests") diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 2f8fc0d2f1..30aa68feb1 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3679,6 +3679,9 @@ STR_6573 :Invisible STR_6574 :Void STR_6575 :Allow special colour schemes STR_6576 :Adds special colours to colour dropdown +STR_6577 :Block brake speed +STR_6578 :Set speed limit for block brakes. In block section mode, adjacent brakes with a slower speed are linked to the block brake. +STR_6579 :Block brakes will be set to default speed when saved as track design ############# # Scenarios # diff --git a/distribution/changelog.txt b/distribution/changelog.txt index cf96997341..9ab4299b52 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,5 +1,6 @@ 0.4.5 (in development) ------------------------------------------------------------------------ +- Feature: [#18713] Block brakes have speed control and brakes slower than adjacent block brakes copy block brake speed when block brake open. - Feature: [#19276] Add Powered Lifthill to Giga Coaster. - Feature: [#19446] Add new color options to color dropdown. - Feature: [#19547] Add large sloped turns to hybrid coaster and single rail coaster. diff --git a/openrct2.proj b/openrct2.proj index 9c4c3c6838..39a6dbe0c4 100644 --- a/openrct2.proj +++ b/openrct2.proj @@ -51,8 +51,8 @@ 64EF7E0B7785602C91AEC66F005C035B05A2133B https://github.com/OpenRCT2/OpenMusic/releases/download/v1.2.0/openmusic.zip c1c9b030c83c12ff628e8ec72fcfe748291a5344 - https://github.com/OpenRCT2/replays/releases/download/v0.0.77/replays.zip - 65CA6D830789C074F575F15E3F9E2C28691C24F8 + https://github.com/OpenRCT2/replays/releases/download/v0.0.78/replays.zip + 31C5D07EED8481D5C6D57F9E4FE9443AAEDE7739 diff --git a/src/openrct2-ui/windows/RideConstruction.cpp b/src/openrct2-ui/windows/RideConstruction.cpp index 14838a87cd..3c7a873438 100644 --- a/src/openrct2-ui/windows/RideConstruction.cpp +++ b/src/openrct2-ui/windows/RideConstruction.cpp @@ -1405,6 +1405,8 @@ public: _currentTrackBankEnd = TRACK_BANK_NONE; _currentTrackLiftHill &= ~CONSTRUCTION_LIFT_HILL_SELECTED; break; + case TrackElemType::BlockBrakes: + _currentBrakeSpeed2 = kRCT2DefaultBlockBrakeSpeed; } _currentTrackCurve = trackPiece | RideConstructionSpecialPieceSelected; WindowRideConstructionUpdateActiveElements(); diff --git a/src/openrct2/GameStateSnapshots.cpp b/src/openrct2/GameStateSnapshots.cpp index 6f0bbcad1f..2bfb86510f 100644 --- a/src/openrct2/GameStateSnapshots.cpp +++ b/src/openrct2/GameStateSnapshots.cpp @@ -455,6 +455,7 @@ struct GameStateSnapshots final : public IGameStateSnapshots COMPARE_FIELD(Vehicle, target_seat_rotation); COMPARE_FIELD(Vehicle, BoatLocation.x); COMPARE_FIELD(Vehicle, BoatLocation.y); + COMPARE_FIELD(Vehicle, BlockBrakeSpeed); } void CompareSpriteDataLitter(const Litter& spriteBase, const Litter& spriteCmp, GameStateSpriteChange& changeData) const diff --git a/src/openrct2/actions/TrackPlaceAction.cpp b/src/openrct2/actions/TrackPlaceAction.cpp index df2715671c..fbfaf0e20f 100644 --- a/src/openrct2/actions/TrackPlaceAction.cpp +++ b/src/openrct2/actions/TrackPlaceAction.cpp @@ -573,12 +573,16 @@ GameActions::Result TrackPlaceAction::Execute() const case TrackElemType::SpinningTunnel: MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_SPINNINGTUNNEL, CoordsXYZ{ mapLoc, trackElement->GetBaseZ() }); break; + case TrackElemType::Brakes: + trackElement->SetBrakeClosed(true); + break; } if (TrackTypeHasSpeedSetting(_trackType)) { trackElement->SetBrakeBoosterSpeed(_brakeSpeed); } - else if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS)) + + if (ride->GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_LANDSCAPE_DOORS)) { trackElement->SetDoorAState(LANDSCAPE_DOOR_CLOSED); trackElement->SetDoorBState(LANDSCAPE_DOOR_CLOSED); diff --git a/src/openrct2/localisation/StringIds.h b/src/openrct2/localisation/StringIds.h index fd5335493c..950d14d41b 100644 --- a/src/openrct2/localisation/StringIds.h +++ b/src/openrct2/localisation/StringIds.h @@ -3977,6 +3977,10 @@ enum : uint16_t STR_CHEAT_ALLOW_SPECIAL_COLOUR_SCHEMES = 6575, STR_CHEAT_ALLOW_SPECIAL_COLOUR_SCHEMES_TIP = 6576, + STR_RIDE_CONSTRUCTION_BLOCK_BRAKE_SPEED = 6577, + STR_RIDE_CONSTRUCTION_BLOCK_BRAKE_SPEED_LIMIT_TIP = 6578, + STR_TRACK_DESIGN_BLOCK_BRAKE_SPEED_RESET = 6579, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working /* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings }; diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index d9cd570809..e293da6707 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 "9" +#define NETWORK_STREAM_VERSION "10" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION diff --git a/src/openrct2/park/ParkFile.cpp b/src/openrct2/park/ParkFile.cpp index f2f904d566..b4746f148b 100644 --- a/src/openrct2/park/ParkFile.cpp +++ b/src/openrct2/park/ParkFile.cpp @@ -64,6 +64,8 @@ #include #include +constexpr const uint32_t BlockBrakeImprovementsVersion = 27; + using namespace OpenRCT2; namespace OpenRCT2 @@ -1092,12 +1094,19 @@ namespace OpenRCT2 else if (it.element->GetType() == TileElementType::Track) { auto* trackElement = it.element->AsTrack(); + auto trackType = trackElement->GetTrackType(); if (TrackTypeMustBeMadeInvisible( - trackElement->GetRideType(), trackElement->GetTrackType(), - os.GetHeader().TargetVersion)) + trackElement->GetRideType(), trackType, os.GetHeader().TargetVersion)) { it.element->SetInvisible(true); } + if (os.GetHeader().TargetVersion < BlockBrakeImprovementsVersion) + { + if (trackType == TrackElemType::Brakes) + trackElement->SetBrakeClosed(true); + if (trackType == TrackElemType::BlockBrakes) + trackElement->SetBrakeBoosterSpeed(kRCT2DefaultBlockBrakeSpeed); + } } else if ( it.element->GetType() == TileElementType::SmallScenery && os.GetHeader().TargetVersion < 23) @@ -2088,7 +2097,18 @@ namespace OpenRCT2 cs.ReadWrite(entity.scream_sound_id); cs.ReadWrite(entity.TrackSubposition); cs.ReadWrite(entity.NumLaps); - cs.ReadWrite(entity.brake_speed); + if (cs.GetMode() == OrcaStream::Mode::READING && os.GetHeader().TargetVersion < BlockBrakeImprovementsVersion) + { + uint8_t brakeSpeed; + cs.ReadWrite(brakeSpeed); + if (entity.GetTrackType() == TrackElemType::BlockBrakes) + brakeSpeed = kRCT2DefaultBlockBrakeSpeed; + entity.brake_speed = brakeSpeed; + } + else + { + cs.ReadWrite(entity.brake_speed); + } cs.ReadWrite(entity.lost_time_out); cs.ReadWrite(entity.vertical_drop_countdown); cs.ReadWrite(entity.var_D3); @@ -2107,6 +2127,14 @@ namespace OpenRCT2 entity.SetFlag(VehicleFlags::Crashed); } } + if (cs.GetMode() == OrcaStream::Mode::READING && os.GetHeader().TargetVersion < BlockBrakeImprovementsVersion) + { + entity.BlockBrakeSpeed = kRCT2DefaultBlockBrakeSpeed; + } + else + { + cs.ReadWrite(entity.BlockBrakeSpeed); + } } template<> void ParkFile::ReadWriteEntity(OrcaStream& os, OrcaStream::ChunkStream& cs, Guest& guest) diff --git a/src/openrct2/park/ParkFile.h b/src/openrct2/park/ParkFile.h index 735f5b688c..977a10de69 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 = 26; + constexpr uint32_t PARK_FILE_CURRENT_VERSION = 27; // The minimum version that is forwards compatible with the current version. - constexpr uint32_t PARK_FILE_MIN_VERSION = 26; + constexpr uint32_t PARK_FILE_MIN_VERSION = 27; // 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/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index b55902d8cc..0d46740fd2 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -1674,6 +1674,8 @@ namespace RCT1 // Skipping IsHighlighted() auto trackType = dst2->GetTrackType(); + // Brakes import as closed to preserve legacy behaviour + dst2->SetBrakeClosed(trackType == TrackElemType::Brakes); if (TrackTypeHasSpeedSetting(trackType)) { dst2->SetBrakeBoosterSpeed(src2->GetBrakeBoosterSpeed()); @@ -2817,6 +2819,7 @@ namespace RCT1 { dst->SetFlag(VehicleFlags::Crashed); } + dst->BlockBrakeSpeed = kRCT2DefaultBlockBrakeSpeed; } template<> void S4Importer::ImportEntity(const RCT12EntityBase& srcBase) diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 0088bce477..9bbbb40242 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -1440,11 +1440,17 @@ namespace RCT2 dst2->SetInverted(src2->IsInverted()); dst2->SetStationIndex(StationIndex::FromUnderlying(src2->GetStationIndex())); dst2->SetHasGreenLight(src2->HasGreenLight()); - dst2->SetBrakeClosed(src2->BlockBrakeClosed()); + // Brakes import as closed to preserve legacy behaviour + dst2->SetBrakeClosed(src2->BlockBrakeClosed() || (trackType == TrackElemType::Brakes)); dst2->SetIsIndestructible(src2->IsIndestructible()); // Skipping IsHighlighted() - if (TrackTypeHasSpeedSetting(trackType)) + // Import block brakes to keep legacy behaviour + if (trackType == TrackElemType::BlockBrakes) + { + dst2->SetBrakeBoosterSpeed(kRCT2DefaultBlockBrakeSpeed); + } + else if (TrackTypeHasSpeedSetting(trackType)) { dst2->SetBrakeBoosterSpeed(src2->GetBrakeBoosterSpeed()); } @@ -2009,6 +2015,10 @@ namespace RCT2 if (tileElement2 != nullptr) dst->SetTrackType(TrackElemType::RotationControlToggle); } + else if (src->GetTrackType() == TrackElemType::BlockBrakes) + { + dst->brake_speed = kRCT2DefaultBlockBrakeSpeed; + } } else { @@ -2075,6 +2085,7 @@ namespace RCT2 { dst->SetFlag(VehicleFlags::Crashed); } + dst->BlockBrakeSpeed = kRCT2DefaultBlockBrakeSpeed; } static uint32_t AdjustScenarioToCurrentTicks(const S6Data& s6, uint32_t tick) diff --git a/src/openrct2/ride/Ride.cpp b/src/openrct2/ride/Ride.cpp index 43dd98109d..ec4a0217c5 100644 --- a/src/openrct2/ride/Ride.cpp +++ b/src/openrct2/ride/Ride.cpp @@ -3050,13 +3050,17 @@ static void RideOpenBlockBrakes(const CoordsXYE& startElement) auto trackType = currentElement.element->AsTrack()->GetTrackType(); switch (trackType) { + case TrackElemType::BlockBrakes: + BlockBrakeSetLinkedBrakesClosed( + CoordsXYZ(currentElement.x, currentElement.y, currentElement.element->GetBaseZ()), + *currentElement.element->AsTrack(), false); + [[fallthrough]]; case TrackElemType::EndStation: case TrackElemType::CableLiftHill: case TrackElemType::Up25ToFlat: case TrackElemType::Up60ToFlat: case TrackElemType::DiagUp25ToFlat: case TrackElemType::DiagUp60ToFlat: - case TrackElemType::BlockBrakes: currentElement.element->AsTrack()->SetBrakeClosed(false); break; } @@ -3064,6 +3068,60 @@ static void RideOpenBlockBrakes(const CoordsXYE& startElement) && currentElement.element != startElement.element); } +/** + * Set the open status of brakes adjacent to the block brake + */ +void BlockBrakeSetLinkedBrakesClosed(const CoordsXYZ& vehicleTrackLocation, TrackElement& trackElement, bool isClosed) +{ + uint8_t brakeSpeed = trackElement.GetBrakeBoosterSpeed(); + + auto tileElement = reinterpret_cast(&trackElement); + 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; + + if (trackBeginEnd.begin_element->AsTrack()->GetTrackType() == TrackElemType::Brakes) + { + trackBeginEnd.begin_element->AsTrack()->SetBrakeClosed( + (trackBeginEnd.begin_element->AsTrack()->GetBrakeBoosterSpeed() >= brakeSpeed) || isClosed); + } + + // 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()->GetTrackType() == TrackElemType::Brakes); +} + /** * * rct2: 0x006B4D26 @@ -3534,7 +3592,8 @@ ResultWithMessage Ride::CreateVehicles(const CoordsXYE& element, bool isApplying { CoordsXYE firstBlock{}; RideCreateVehiclesFindFirstBlock(*this, &firstBlock); - MoveTrainsToBlockBrakes(firstBlock.element->AsTrack()); + MoveTrainsToBlockBrakes( + { firstBlock.x, firstBlock.y, firstBlock.element->GetBaseZ() }, *firstBlock.element->AsTrack()); } else { @@ -3567,7 +3626,7 @@ ResultWithMessage Ride::CreateVehicles(const CoordsXYE& element, bool isApplying * preceding that block. * rct2: 0x006DDF9C */ -void Ride::MoveTrainsToBlockBrakes(TrackElement* firstBlock) +void Ride::MoveTrainsToBlockBrakes(const CoordsXYZ& firstBlockPosition, TrackElement& firstBlock) { for (int32_t i = 0; i < NumTrains; i++) { @@ -3578,7 +3637,8 @@ void Ride::MoveTrainsToBlockBrakes(TrackElement* firstBlock) // At this point, all vehicles have state of MovingToEndOfStation, which slowly moves forward at a constant speed // regardless of incline. The first vehicle stops at the station immediately, while all other vehicles seek forward // until they reach a closed block brake. The block brake directly before the station is set to closed every frame - // because the trains will open the block brake when the tail leaves the station. + // because the trains will open the block brake when the tail leaves the station. Brakes have no effect at this time, so + // do not set linked brakes when closing the first block. train->UpdateTrackMotion(nullptr); if (i == 0) @@ -3597,7 +3657,7 @@ void Ride::MoveTrainsToBlockBrakes(TrackElement* firstBlock) break; } - firstBlock->SetBrakeClosed(true); + firstBlock.SetBrakeClosed(true); for (Vehicle* car = train; car != nullptr; car = GetEntity(car->next_vehicle_on_train)) { car->velocity = 0; @@ -3607,8 +3667,13 @@ void Ride::MoveTrainsToBlockBrakes(TrackElement* firstBlock) } } while (!(train->UpdateTrackMotion(nullptr) & VEHICLE_UPDATE_MOTION_TRACK_FLAG_VEHICLE_AT_BLOCK_BRAKE)); - // All vehicles are in position, set the block brake directly before the station one last time - firstBlock->SetBrakeClosed(true); + // All vehicles are in position, set the block brake directly before the station one last time and make sure the brakes + // are set appropriately + firstBlock.SetBrakeClosed(true); + if (firstBlock.GetTrackType() == TrackElemType::BlockBrakes) + { + BlockBrakeSetLinkedBrakesClosed(firstBlockPosition, firstBlock, true); + } for (Vehicle* car = train; car != nullptr; car = GetEntity(car->next_vehicle_on_train)) { car->ClearFlag(VehicleFlags::CollisionDisabled); diff --git a/src/openrct2/ride/Ride.h b/src/openrct2/ride/Ride.h index bc61a3c762..8a4c8b0dd7 100644 --- a/src/openrct2/ride/Ride.h +++ b/src/openrct2/ride/Ride.h @@ -304,7 +304,7 @@ private: void Update(); void UpdateQueueLength(StationIndex stationIndex); ResultWithMessage CreateVehicles(const CoordsXYE& element, bool isApplying); - void MoveTrainsToBlockBrakes(TrackElement* firstBlock); + void MoveTrainsToBlockBrakes(const CoordsXYZ& firstBlockPosition, TrackElement& firstBlock); money64 CalculateIncomePerHour() const; void ChainQueues() const; void ConstructMissingEntranceOrExit() const; @@ -1067,6 +1067,8 @@ money64 RideEntranceExitPlaceGhost( ResultWithMessage RideAreAllPossibleEntrancesAndExitsBuilt(const Ride& ride); void RideFixBreakdown(Ride& ride, int32_t reliabilityIncreaseFactor); +void BlockBrakeSetLinkedBrakesClosed(const CoordsXYZ& vehicleTrackLocation, TrackElement& tileElement, bool isOpen); + uint8_t RideEntryGetVehicleAtPosition(int32_t rideEntryIndex, int32_t numCarsPerTrain, int32_t position); void RideUpdateVehicleColours(const Ride& ride); diff --git a/src/openrct2/ride/Track.cpp b/src/openrct2/ride/Track.cpp index ebcf61b5b7..8343c30dcf 100644 --- a/src/openrct2/ride/Track.cpp +++ b/src/openrct2/ride/Track.cpp @@ -655,7 +655,7 @@ bool TrackElementIsCovered(track_type_t trackElementType) bool TrackTypeHasSpeedSetting(track_type_t trackType) { - return trackType == TrackElemType::Brakes || trackType == TrackElemType::Booster; + return trackType == TrackElemType::Brakes || trackType == TrackElemType::Booster || trackType == TrackElemType::BlockBrakes; } bool TrackTypeIsHelix(track_type_t trackType) diff --git a/src/openrct2/ride/Track.h b/src/openrct2/ride/Track.h index 51240c5519..aa77231528 100644 --- a/src/openrct2/ride/Track.h +++ b/src/openrct2/ride/Track.h @@ -18,7 +18,9 @@ constexpr const uint32_t RideConstructionSpecialPieceSelected = 0x10000; -constexpr const int32_t BLOCK_BRAKE_BASE_SPEED = 0x20364; +constexpr const uint8_t kRCT2DefaultBlockBrakeSpeed = 2; +constexpr const int32_t kBlockBrakeBaseSpeed = 0x20364; +constexpr const int32_t kBlockBrakeSpeedOffset = kBlockBrakeBaseSpeed - (kRCT2DefaultBlockBrakeSpeed << 16); using track_type_t = uint16_t; using roll_type_t = uint8_t; diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index e13c139b63..6a47357eed 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -182,6 +182,8 @@ ResultWithMessage TrackDesign::CreateTrackDesignTrack(TrackDesignState& tds, con return { false, STR_TRACK_TOO_LARGE_OR_TOO_MUCH_SCENERY }; } + StringId warningMessage = STR_NONE; + RideGetStartOfTrack(&trackElement); int32_t z = trackElement.element->GetBaseZ(); @@ -222,7 +224,8 @@ ResultWithMessage TrackDesign::CreateTrackDesignTrack(TrackDesignState& tds, con track.type = trackElement.element->AsTrack()->GetTrackType(); uint8_t trackFlags; - if (TrackTypeHasSpeedSetting(track.type)) + // This if-else block only applies to td6. New track design format will always encode speed and seat rotation. + if (TrackTypeHasSpeedSetting(track.type) && track.type != TrackElemType::BlockBrakes) { trackFlags = trackElement.element->AsTrack()->GetBrakeBoosterSpeed() >> 1; } @@ -231,6 +234,13 @@ ResultWithMessage TrackDesign::CreateTrackDesignTrack(TrackDesignState& tds, con trackFlags = trackElement.element->AsTrack()->GetSeatRotation(); } + // This warning will not apply to new track design format + if (track.type == TrackElemType::BlockBrakes + && trackElement.element->AsTrack()->GetBrakeBoosterSpeed() != kRCT2DefaultBlockBrakeSpeed) + { + warningMessage = STR_TRACK_DESIGN_BLOCK_BRAKE_SPEED_RESET; + } + if (trackElement.element->AsTrack()->HasChain()) trackFlags |= RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT; trackFlags |= trackElement.element->AsTrack()->GetColourScheme() << 4; @@ -349,7 +359,7 @@ ResultWithMessage TrackDesign::CreateTrackDesignTrack(TrackDesignState& tds, con space_required_x = ((tds.PreviewMax.x - tds.PreviewMin.x) / 32) + 1; space_required_y = ((tds.PreviewMax.y - tds.PreviewMin.y) / 32) + 1; - return { true }; + return { true, warningMessage }; } ResultWithMessage TrackDesign::CreateTrackDesignMaze(TrackDesignState& tds, const Ride& ride) @@ -1617,7 +1627,17 @@ static GameActions::Result TrackDesignPlaceRide(TrackDesignState& tds, TrackDesi // di int16_t tempZ = newCoords.z - trackCoordinates->z_begin; uint32_t trackColour = (track.flags >> 4) & 0x3; - uint32_t brakeSpeed = (track.flags & 0x0F) * 2; + uint32_t brakeSpeed; + // RCT2-created track designs write brake speed to all tracks; block brake speed must be treated as + // garbage data. + if (trackType == TrackElemType::BlockBrakes) + { + brakeSpeed = kRCT2DefaultBlockBrakeSpeed; + } + else + { + brakeSpeed = (track.flags & 0x0F) * 2; + } uint32_t seatRotation = track.flags & 0x0F; int32_t liftHillAndAlternativeState = 0; diff --git a/src/openrct2/ride/Vehicle.cpp b/src/openrct2/ride/Vehicle.cpp index 53b79184ff..fde8843af3 100644 --- a/src/openrct2/ride/Vehicle.cpp +++ b/src/openrct2/ride/Vehicle.cpp @@ -6004,15 +6004,14 @@ void Vehicle::ApplyNonStopBlockBrake() if (velocity >= 0) { // If the vehicle is below the speed limit - if (velocity <= BLOCK_BRAKE_BASE_SPEED) + if (velocity <= kBlockBrakeBaseSpeed) { // Boost it to the fixed block brake speed - velocity = BLOCK_BRAKE_BASE_SPEED; + velocity = kBlockBrakeBaseSpeed; acceleration = 0; } - else + else if (velocity > (brake_speed << 16) + kBlockBrakeSpeedOffset) { - // Slow it down till the fixed block brake speed velocity -= velocity >> 4; acceleration = 0; } @@ -6185,12 +6184,14 @@ static void block_brakes_open_previous_section( MapInvalidateElement(location, reinterpret_cast(trackElement)); auto trackType = trackElement->GetTrackType(); - if (trackType == TrackElemType::BlockBrakes || trackType == TrackElemType::EndStation) + if (trackType == TrackElemType::EndStation) { - if (ride.IsBlockSectioned()) - { - OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::BlockBrakeClose, location); - } + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::BlockBrakeClose, location); + } + else if (trackType == TrackElemType::BlockBrakes) + { + OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::BlockBrakeClose, location); + BlockBrakeSetLinkedBrakesClosed(location, *trackElement, false); } } @@ -6981,7 +6982,7 @@ void Vehicle::UpdateLandscapeDoorBackwards() const static void vehicle_update_play_water_splash_sound() { - if (_vehicleVelocityF64E08 <= BLOCK_BRAKE_BASE_SPEED) + if (_vehicleVelocityF64E08 <= kBlockBrakeBaseSpeed) { return; } @@ -7377,6 +7378,60 @@ void Vehicle::Sub6DBF3E() } } +/** + * Determine whether to use block brake speed or brake speed. If block brake is closed or no block brake present, use the + * brake's speed; if block brake is open, use maximum of brake speed or block brake speed. + */ +uint8_t Vehicle::ChooseBrakeSpeed() const +{ + if (GetTrackType() != TrackElemType::Brakes) + return brake_speed; + auto trackElement = MapGetTrackElementAtOfTypeSeq(TrackLocation, GetTrackType(), 0); + if (trackElement != nullptr) + { + if (trackElement->AsTrack()->IsBrakeClosed()) + return brake_speed; + else + return std::max(brake_speed, BlockBrakeSpeed); + } + return brake_speed; +} + +/** + * Populate the vehicle's brake_speed and BlockBrakeSpeed values. + */ +void Vehicle::PopulateBrakeSpeed(const CoordsXYZ& vehicleTrackLocation, TrackElement& brake) +{ + auto trackSpeed = brake.GetBrakeBoosterSpeed(); + brake_speed = trackSpeed; + if (brake.GetTrackType() != TrackElemType::Brakes) + { + BlockBrakeSpeed = trackSpeed; + return; + } + // As soon as feasible, encode block brake speed into track element so the lookforward can be skipped here. + + CoordsXYE output = CoordsXYE(vehicleTrackLocation.x, vehicleTrackLocation.y, reinterpret_cast(&brake)); + int32_t outputZ = vehicleTrackLocation.z; + uint16_t timeoutCount = 256; + do + { + if (output.element->AsTrack()->GetTrackType() == TrackElemType::BlockBrakes) + { + BlockBrakeSpeed = output.element->AsTrack()->GetBrakeBoosterSpeed(); + return; + } + if (output.element->AsTrack()->GetTrackType() != TrackElemType::Brakes) + { + break; + } + timeoutCount--; + } while (TrackBlockGetNext(&output, &output, &outputZ, nullptr) && timeoutCount); + + // If block brake is not found, use the track's speed + BlockBrakeSpeed = trackSpeed; +} + /** * * rct2: 0x006DB08C @@ -7413,6 +7468,10 @@ bool Vehicle::UpdateTrackMotionForwardsGetNewTrack(uint16_t trackType, const Rid } MapInvalidateElement(TrackLocation, tileElement); block_brakes_open_previous_section(curRide, TrackLocation, tileElement); + if (trackType == TrackElemType::BlockBrakes) + { + BlockBrakeSetLinkedBrakesClosed(TrackLocation, *tileElement->AsTrack(), true); + } } } @@ -7542,7 +7601,7 @@ bool Vehicle::UpdateTrackMotionForwardsGetNewTrack(uint16_t trackType, const Rid } SetTrackDirection(location.direction); SetTrackType(trackType); - brake_speed = tileElement->AsTrack()->GetBrakeBoosterSpeed(); + PopulateBrakeSpeed(TrackLocation, *tileElement->AsTrack()); if (trackType == TrackElemType::OnRidePhoto) { trigger_on_ride_photo(TrackLocation, tileElement); @@ -7589,8 +7648,9 @@ Loc6DAEB9: && curRide.breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE; if (!hasBrakesFailure || curRide.mechanic_status == RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES) { - auto brakeSpeed = brake_speed << 16; - if (brakeSpeed < _vehicleVelocityF64E08) + auto brakeSpeed = ChooseBrakeSpeed(); + + if ((brakeSpeed << 16) < _vehicleVelocityF64E08) { acceleration = -_vehicleVelocityF64E08 * 16; } @@ -7947,7 +8007,7 @@ bool Vehicle::UpdateTrackMotionBackwardsGetNewTrack(uint16_t trackType, const Ri direction &= 3; SetTrackType(trackType); SetTrackDirection(direction); - brake_speed = tileElement->AsTrack()->GetBrakeBoosterSpeed(); + PopulateBrakeSpeed(TrackLocation, *tileElement->AsTrack()); // There are two bytes before the move info list uint16_t trackTotalProgress = GetTrackProgress(); @@ -7978,7 +8038,9 @@ bool Vehicle::UpdateTrackMotionBackwards(const CarEntry* carEntry, const Ride& c if (trackType == TrackElemType::Brakes) { - if (-(brake_speed << 16) > _vehicleVelocityF64E08) + auto brakeSpeed = ChooseBrakeSpeed(); + + if (-(brakeSpeed << 16) > _vehicleVelocityF64E08) { acceleration = _vehicleVelocityF64E08 * -16; } @@ -9400,4 +9462,5 @@ void Vehicle::Serialise(DataSerialiser& stream) stream << seat_rotation; stream << target_seat_rotation; stream << BoatLocation; + stream << BlockBrakeSpeed; } diff --git a/src/openrct2/ride/Vehicle.h b/src/openrct2/ride/Vehicle.h index 55fc833ecf..a3f98f6ac1 100644 --- a/src/openrct2/ride/Vehicle.h +++ b/src/openrct2/ride/Vehicle.h @@ -210,6 +210,7 @@ struct Vehicle : EntityBase uint8_t seat_rotation; uint8_t target_seat_rotation; CoordsXY BoatLocation; + uint8_t BlockBrakeSpeed; constexpr bool IsHead() const { @@ -377,6 +378,8 @@ private: void UpdateLandscapeDoor() const; void UpdateLandscapeDoorBackwards() const; int32_t CalculateRiderBraking() const; + uint8_t ChooseBrakeSpeed() const; + void PopulateBrakeSpeed(const CoordsXYZ& vehicleTrackLocation, TrackElement& brake); void Loc6DCE02(const Ride& curRide); };