diff --git a/src/openrct2-ui/windows/TrackDesignPlace.cpp b/src/openrct2-ui/windows/TrackDesignPlace.cpp index 8ddd8318e8..f7a7e7aa5e 100644 --- a/src/openrct2-ui/windows/TrackDesignPlace.cpp +++ b/src/openrct2-ui/windows/TrackDesignPlace.cpp @@ -441,14 +441,8 @@ private: uint8_t curTrackRotation = rotation; for (const auto& trackElement : td6->track_elements) { - int32_t trackType = trackElement.type; - if (trackType == TrackElemType::InvertedUp90ToFlatQuarterLoopAlias) - { - trackType = TrackElemType::MultiDimInvertedUp90ToFlatQuarterLoop; - } - // Follow a single track piece shape - const auto& ted = GetTrackElementDescriptor(trackType); + const auto& ted = GetTrackElementDescriptor(trackElement.Type); const PreviewTrack* trackBlock = ted.Block; while (trackBlock->index != 255) { diff --git a/src/openrct2/core/DataSerialiserTraits.h b/src/openrct2/core/DataSerialiserTraits.h index ee1663b239..01f504122a 100644 --- a/src/openrct2/core/DataSerialiserTraits.h +++ b/src/openrct2/core/DataSerialiserTraits.h @@ -675,18 +675,26 @@ template<> struct DataSerializerTraitsT { static void encode(OpenRCT2::IStream* stream, const TrackDesignTrackElement& val) { - stream->Write(&val.flags); - stream->Write(&val.type); + stream->Write(&val.Type); + stream->Write(&val.Flags); + stream->Write(&val.ColourScheme); + stream->Write(&val.StationIndex); + stream->Write(&val.BrakeBoosterSpeed); + stream->Write(&val.SeatRotation); } static void decode(OpenRCT2::IStream* stream, TrackDesignTrackElement& val) { - stream->Read(&val.flags); - stream->Read(&val.type); + stream->Read(&val.Type); + stream->Read(&val.Flags); + stream->Read(&val.ColourScheme); + stream->Read(&val.StationIndex); + stream->Read(&val.BrakeBoosterSpeed); + stream->Read(&val.SeatRotation); } static void log(OpenRCT2::IStream* stream, const TrackDesignTrackElement& val) { char msg[128] = {}; - snprintf(msg, sizeof(msg), "TrackDesignTrackElement(type = %d, flags = %d)", val.type, val.flags); + snprintf(msg, sizeof(msg), "TrackDesignTrackElement(type = %d, flags = %d)", val.Type, val.Flags); stream->Write(msg, strlen(msg)); } }; @@ -924,4 +932,4 @@ template<> struct DataSerializerTraitsT msg, sizeof(msg), "Banner(x = %d, y = %d, text = %s)", banner.position.x, banner.position.y, banner.text.c_str()); stream->Write(msg, strlen(msg)); } -}; \ No newline at end of file +}; diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 679b7f8e6a..da9896969a 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/rct1/T4Importer.cpp b/src/openrct2/rct1/T4Importer.cpp index 4838cdd56f..30868e13f7 100644 --- a/src/openrct2/rct1/T4Importer.cpp +++ b/src/openrct2/rct1/T4Importer.cpp @@ -266,8 +266,8 @@ namespace RCT1 _stream.SetPosition(_stream.GetPosition() - 1); _stream.Read(&t4TrackElement, sizeof(TD46TrackElement)); TrackDesignTrackElement trackElement{}; - trackElement.type = RCT1TrackTypeToOpenRCT2(t4TrackElement.Type, td->type); - trackElement.flags = t4TrackElement.Flags; + trackElement.Type = RCT1TrackTypeToOpenRCT2(t4TrackElement.Type, td->type); + ConvertFromTD46Flags(trackElement, t4TrackElement.Flags); td->track_elements.push_back(trackElement); } } diff --git a/src/openrct2/rct12/RCT12.cpp b/src/openrct2/rct12/RCT12.cpp index be792d3243..8c34d9d543 100644 --- a/src/openrct2/rct12/RCT12.cpp +++ b/src/openrct2/rct12/RCT12.cpp @@ -16,6 +16,7 @@ #include "../rct2/RCT2.h" #include "../ride/Ride.h" #include "../ride/Track.h" +#include "../ride/TrackDesign.h" #include "../scenario/Scenario.h" #include "../world/Banner.h" #include "../world/Footpath.h" @@ -869,3 +870,57 @@ ResearchItem RCT12ResearchItem::ToResearchItem() const return newResearchItem; } + +void ConvertFromTD46Flags(TrackDesignTrackElement& target, uint8_t flags) +{ + target.BrakeBoosterSpeed = kRCT2DefaultBlockBrakeSpeed; + if (TrackTypeIsStation(target.Type)) + { + auto stationIndex = flags & EnumValue(TD46Flags::StationId); + target.StationIndex = StationIndex::FromUnderlying(stationIndex); + } + else + { + auto speedOrSeatRotation = flags & EnumValue(TD46Flags::SpeedOrSeatRotation); + if (TrackTypeHasSpeedSetting(target.Type) && target.Type != TrackElemType::BlockBrakes) + { + target.BrakeBoosterSpeed = speedOrSeatRotation << 1; + } + else + { + target.SeatRotation = speedOrSeatRotation; + } + } + + target.ColourScheme = (flags & EnumValue(TD46Flags::ColourScheme)) >> 4; + if (flags & EnumValue(TD46Flags::IsInverted)) + target.SetFlag(TrackDesignTrackElementFlag::IsInverted); + if (flags & EnumValue(TD46Flags::HasChain)) + target.SetFlag(TrackDesignTrackElementFlag::HasChain); +} + +uint8_t ConvertToTD46Flags(const TrackDesignTrackElement& source) +{ + uint8_t trackFlags = 0; + if (TrackTypeIsStation(source.Type)) + { + trackFlags = (source.StationIndex.ToUnderlying() & EnumValue(TD46Flags::StationId)); + } + else if (TrackTypeHasSpeedSetting(source.Type) && source.Type != TrackElemType::BlockBrakes) + { + trackFlags = (source.BrakeBoosterSpeed >> 1); + } + else + { + trackFlags = source.SeatRotation; + } + + trackFlags |= source.ColourScheme << 4; + + if (source.HasFlag(TrackDesignTrackElementFlag::HasChain)) + trackFlags |= EnumValue(TD46Flags::HasChain); + if (source.HasFlag(TrackDesignTrackElementFlag::IsInverted)) + trackFlags |= EnumValue(TD46Flags::IsInverted); + + return trackFlags; +} diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h index f40a622fb0..1465478b11 100644 --- a/src/openrct2/rct12/RCT12.h +++ b/src/openrct2/rct12/RCT12.h @@ -74,6 +74,8 @@ constexpr uint8_t RCT12PeepThoughtItemNone = std::numeric_limits::max() constexpr uint8_t RCT12GuestsInParkHistoryFactor = 20; constexpr uint8_t RCT12ParkHistoryUndefined = std::numeric_limits::max(); +struct TrackDesignTrackElement; + enum class RCT12TrackDesignVersion : uint8_t { TD4, @@ -900,3 +902,15 @@ template std::vector RCT12GetRidesBeenOn(T* srcPeep) } return ridesBeenOn; } + +enum class TD46Flags : uint8_t +{ + StationId = 0b00000011, + SpeedOrSeatRotation = 0b00001111, + ColourScheme = 0b00110000, + IsInverted = 0b01000000, + HasChain = 0b10000000, +}; + +void ConvertFromTD46Flags(TrackDesignTrackElement& target, uint8_t flags); +uint8_t ConvertToTD46Flags(const TrackDesignTrackElement& source); diff --git a/src/openrct2/rct2/T6Exporter.cpp b/src/openrct2/rct2/T6Exporter.cpp index ec41a4f12e..343b1a193c 100644 --- a/src/openrct2/rct2/T6Exporter.cpp +++ b/src/openrct2/rct2/T6Exporter.cpp @@ -111,13 +111,14 @@ namespace RCT2 { for (const auto& trackElement : _trackDesign->track_elements) { - auto trackType = OpenRCT2TrackTypeToRCT2(trackElement.type); + auto trackType = OpenRCT2TrackTypeToRCT2(trackElement.Type); if (trackType == TrackElemType::MultiDimInvertedUp90ToFlatQuarterLoop) { trackType = TrackElemType::InvertedUp90ToFlatQuarterLoopAlias; } tempStream.WriteValue(static_cast(trackType)); - tempStream.WriteValue(trackElement.flags); + auto flags = ConvertToTD46Flags(trackElement); + tempStream.WriteValue(flags); } tempStream.WriteValue(0xFF); diff --git a/src/openrct2/rct2/T6Importer.cpp b/src/openrct2/rct2/T6Importer.cpp index 5a767f0357..82a9b69084 100644 --- a/src/openrct2/rct2/T6Importer.cpp +++ b/src/openrct2/rct2/T6Importer.cpp @@ -171,8 +171,8 @@ namespace RCT2 trackType = TrackElemType::MultiDimInvertedUp90ToFlatQuarterLoop; } - trackElement.type = trackType; - trackElement.flags = t6TrackElement.Flags; + trackElement.Type = trackType; + ConvertFromTD46Flags(trackElement, t6TrackElement.Flags); td->track_elements.push_back(trackElement); } diff --git a/src/openrct2/ride/Track.h b/src/openrct2/ride/Track.h index 8e33603349..a650463398 100644 --- a/src/openrct2/ride/Track.h +++ b/src/openrct2/ride/Track.h @@ -82,12 +82,6 @@ enum RCT_PREVIEW_TRACK_FLAG_IS_VERTICAL = (1 << 2), }; -enum -{ - TRACK_ELEMENT_FLAG_TERMINAL_STATION = 1 << 3, - TD6_TRACK_ELEMENT_FLAG_INVERTED = 1 << 6, -}; - enum { TRACK_ELEMENT_FLAGS2_CHAIN_LIFT = 1 << 0, diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index c400d922d2..8e1749ccfe 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -214,43 +214,35 @@ ResultWithMessage TrackDesign::CreateTrackDesignTrack(TrackDesignState& tds, con do { + const auto& element = trackElement.element->AsTrack(); + // Remove this check for new track design format - if (trackElement.element->AsTrack()->GetTrackType() > TrackElemType::HighestAlias) + if (element->GetTrackType() > TrackElemType::HighestAlias) { return { false, STR_TRACK_ELEM_UNSUPPORTED_TD6 }; } TrackDesignTrackElement track{}; - track.type = trackElement.element->AsTrack()->GetTrackType(); - - uint8_t trackFlags; - // 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; - } - else - { - trackFlags = trackElement.element->AsTrack()->GetSeatRotation(); - } + track.Type = element->GetTrackType(); + track.ColourScheme = element->GetColourScheme(); + track.StationIndex = element->GetStationIndex(); + track.BrakeBoosterSpeed = element->GetBrakeBoosterSpeed(); + track.SeatRotation = element->GetSeatRotation(); // This warning will not apply to new track design format - if (track.type == TrackElemType::BlockBrakes - && trackElement.element->AsTrack()->GetBrakeBoosterSpeed() != kRCT2DefaultBlockBrakeSpeed) + if (track.Type == TrackElemType::BlockBrakes && element->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; - if (ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE) - && trackElement.element->AsTrack()->IsInverted()) + if (element->HasChain()) + track.SetFlag(TrackDesignTrackElementFlag::HasChain); + + if (ride.GetRideTypeDescriptor().HasFlag(RIDE_TYPE_FLAG_HAS_ALTERNATIVE_TRACK_TYPE) && element->IsInverted()) { - trackFlags |= TD6_TRACK_ELEMENT_FLAG_INVERTED; + track.SetFlag(TrackDesignTrackElementFlag::IsInverted); } - track.flags = trackFlags; track_elements.push_back(track); if (!TrackBlockGetNext(&trackElement, &trackElement, nullptr, nullptr)) @@ -898,8 +890,8 @@ static void TrackDesignMirrorRide(TrackDesign* td6) { for (auto& track : td6->track_elements) { - const auto& ted = GetTrackElementDescriptor(track.type); - track.type = ted.MirrorElement; + const auto& ted = GetTrackElementDescriptor(track.Type); + track.Type = ted.MirrorElement; } for (auto& entrance : td6->entrance_elements) @@ -1595,7 +1587,7 @@ static GameActions::Result TrackDesignPlaceRide(TrackDesignState& tds, TrackDesi auto newCoords = origin; for (const auto& track : td6->track_elements) { - auto trackType = track.type; + auto trackType = track.Type; const auto& ted = GetTrackElementDescriptor(trackType); TrackDesignUpdatePreviewBounds(tds, newCoords); @@ -1632,26 +1624,13 @@ 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; - // 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; - if (track.flags & RCT12_TRACK_ELEMENT_TYPE_FLAG_CHAIN_LIFT) + if (track.HasFlag(TrackDesignTrackElementFlag::HasChain)) { liftHillAndAlternativeState |= 1; } - if (track.flags & TD6_TRACK_ELEMENT_FLAG_INVERTED) + if (track.HasFlag(TrackDesignTrackElementFlag::IsInverted)) { liftHillAndAlternativeState |= 2; } @@ -1678,8 +1657,8 @@ static GameActions::Result TrackDesignPlaceRide(TrackDesignState& tds, TrackDesi } auto trackPlaceAction = TrackPlaceAction( - ride.id, trackType, ride.type, { newCoords, tempZ, static_cast(rotation) }, brakeSpeed, - trackColour, seatRotation, liftHillAndAlternativeState, true); + ride.id, trackType, ride.type, { newCoords, tempZ, static_cast(rotation) }, + track.BrakeBoosterSpeed, track.ColourScheme, track.SeatRotation, liftHillAndAlternativeState, true); trackPlaceAction.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&trackPlaceAction) diff --git a/src/openrct2/ride/TrackDesign.h b/src/openrct2/ride/TrackDesign.h index f5c3d0c2ce..d795eaa9bb 100644 --- a/src/openrct2/ride/TrackDesign.h +++ b/src/openrct2/ride/TrackDesign.h @@ -62,15 +62,36 @@ struct TrackDesignSceneryElement } }; -/** - * Track design structure. - */ +enum class TrackDesignTrackElementFlag : uint8_t +{ + HasChain = (1 << 0), + IsInverted = (1 << 1), + IsCovered = (1 << 2), // Reserved +}; -/* Track Element entry size: 0x03 */ struct TrackDesignTrackElement { - track_type_t type; // 0x00 - uint8_t flags; // 0x02 + track_type_t Type = 0; + uint8_t Flags = 0; + uint8_t ColourScheme = 0; + ::StationIndex StationIndex = StationIndex::FromUnderlying(0); + uint8_t BrakeBoosterSpeed = 0; + uint8_t SeatRotation = 4; + + constexpr bool HasFlag(const TrackDesignTrackElementFlag flag) const + { + return Flags & EnumValue(flag); + } + + constexpr void SetFlag(const TrackDesignTrackElementFlag flag) + { + Flags |= EnumValue(flag); + } + + constexpr void ClearFlag(const TrackDesignTrackElementFlag flag) + { + Flags &= ~EnumValue(flag); + } }; /* Maze Element entry size: 0x04 */