mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-26 00:04:43 +01:00
202 lines
9.4 KiB
C++
202 lines
9.4 KiB
C++
/*****************************************************************************
|
|
* Copyright (c) 2014-2025 OpenRCT2 developers
|
|
*
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
*
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
*****************************************************************************/
|
|
|
|
#include "T6Exporter.h"
|
|
|
|
#include "../Context.h"
|
|
#include "../Diagnostic.h"
|
|
#include "../core/FileStream.h"
|
|
#include "../core/MemoryStream.h"
|
|
#include "../localisation/StringIds.h"
|
|
#include "../object/ObjectList.h"
|
|
#include "../rct12/SawyerChunkWriter.h"
|
|
#include "../rct2/RCT2.h"
|
|
#include "../ride/Ride.h"
|
|
#include "../ride/RideData.h"
|
|
#include "../ride/Station.h"
|
|
#include "../ride/Track.h"
|
|
#include "../ride/TrackData.h"
|
|
#include "../ride/TrackDesign.h"
|
|
#include "../ride/TrackDesignRepository.h"
|
|
#include "../windows/Intent.h"
|
|
|
|
#include <functional>
|
|
|
|
namespace OpenRCT2::RCT2
|
|
{
|
|
T6Exporter::T6Exporter(const TrackDesign& trackDesign)
|
|
: _trackDesign(trackDesign)
|
|
{
|
|
}
|
|
|
|
bool T6Exporter::SaveTrack(const utf8* path)
|
|
{
|
|
try
|
|
{
|
|
auto fs = OpenRCT2::FileStream(path, OpenRCT2::FileMode::write);
|
|
return SaveTrack(&fs);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
LOG_ERROR("Unable to save track design: %s", e.what());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool T6Exporter::SaveTrack(OpenRCT2::IStream* stream)
|
|
{
|
|
auto& rtd = GetRideTypeDescriptor(_trackDesign.trackAndVehicle.rtdIndex);
|
|
auto td6rideType = OpenRCT2RideTypeToRCT2RideType(_trackDesign.trackAndVehicle.rtdIndex);
|
|
OpenRCT2::MemoryStream tempStream;
|
|
tempStream.WriteValue<uint8_t>(td6rideType);
|
|
tempStream.WriteValue<uint8_t>(0);
|
|
tempStream.WriteValue<uint32_t>(0);
|
|
tempStream.WriteValue<uint8_t>(static_cast<uint8_t>(_trackDesign.operation.rideMode));
|
|
tempStream.WriteValue<uint8_t>(EnumValue(_trackDesign.appearance.vehicleColourSettings) | (2 << 2));
|
|
for (auto i = 0; i < RCT2::Limits::kMaxVehicleColours; i++)
|
|
{
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.appearance.vehicleColours[i].Body);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.appearance.vehicleColours[i].Trim);
|
|
}
|
|
tempStream.WriteValue<uint8_t>(0);
|
|
auto entranceStyle = GetStationStyleFromIdentifier(_trackDesign.appearance.stationObjectIdentifier);
|
|
tempStream.WriteValue<uint8_t>(entranceStyle);
|
|
// The 512 added is to enforce correctly rounding up, as integer division will truncate.
|
|
uint16_t _totalAirTime = std::min<uint16_t>(255, ((_trackDesign.statistics.totalAirTime * 123) + 512) / 1024);
|
|
tempStream.WriteValue<uint8_t>(_totalAirTime);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.operation.departFlags);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.trackAndVehicle.numberOfTrains);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.trackAndVehicle.numberOfCarsPerTrain);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.operation.minWaitingTime);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.operation.maxWaitingTime);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.operation.operationSetting);
|
|
tempStream.WriteValue<int8_t>(_trackDesign.statistics.maxSpeed);
|
|
tempStream.WriteValue<int8_t>(_trackDesign.statistics.averageSpeed);
|
|
tempStream.WriteValue<uint16_t>(_trackDesign.statistics.rideLength);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.statistics.maxPositiveVerticalG / kTD46GForcesMultiplier);
|
|
tempStream.WriteValue<int8_t>(_trackDesign.statistics.maxNegativeVerticalG / kTD46GForcesMultiplier);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.statistics.maxLateralG / kTD46GForcesMultiplier);
|
|
|
|
if (rtd.specialType == RtdSpecialType::miniGolf)
|
|
{
|
|
auto numHoles = std::min<uint8_t>(31, _trackDesign.statistics.holes);
|
|
tempStream.WriteValue<uint8_t>(numHoles & kRCT12InversionAndHoleMask);
|
|
}
|
|
else
|
|
{
|
|
auto numInversions = std::min<uint8_t>(31, _trackDesign.statistics.inversions);
|
|
tempStream.WriteValue<uint8_t>(numInversions & kRCT12InversionAndHoleMask);
|
|
}
|
|
|
|
tempStream.WriteValue<uint8_t>(std::min<uint8_t>(63, _trackDesign.statistics.drops) & kRCT12RideNumDropsMask);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.statistics.highestDropHeight);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.statistics.ratings.excitement / kTD46RatingsMultiplier);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.statistics.ratings.intensity / kTD46RatingsMultiplier);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.statistics.ratings.nausea / kTD46RatingsMultiplier);
|
|
tempStream.WriteValue<money16>(ToMoney16(_trackDesign.statistics.upkeepCost));
|
|
for (auto i = 0; i < Limits::kNumColourSchemes; i++)
|
|
{
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.appearance.trackColours[i].main);
|
|
}
|
|
for (auto i = 0; i < Limits::kNumColourSchemes; i++)
|
|
{
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.appearance.trackColours[i].additional);
|
|
}
|
|
for (auto i = 0; i < Limits::kNumColourSchemes; i++)
|
|
{
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.appearance.trackColours[i].supports);
|
|
}
|
|
tempStream.WriteValue<uint32_t>(0);
|
|
tempStream.Write(&_trackDesign.trackAndVehicle.vehicleObject.Entry, sizeof(RCTObjectEntry));
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.statistics.spaceRequired.x);
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.statistics.spaceRequired.y);
|
|
for (auto i = 0; i < RCT2::Limits::kMaxVehicleColours; i++)
|
|
{
|
|
tempStream.WriteValue<uint8_t>(_trackDesign.appearance.vehicleColours[i].Tertiary);
|
|
}
|
|
auto liftSpeed = std::min<uint8_t>(31, _trackDesign.operation.liftHillSpeed);
|
|
auto numCircuits = std::min<uint8_t>(7, _trackDesign.operation.numCircuits);
|
|
tempStream.WriteValue<uint8_t>(liftSpeed | (numCircuits << 5));
|
|
|
|
if (rtd.specialType == RtdSpecialType::maze)
|
|
{
|
|
for (const auto& mazeElement : _trackDesign.mazeElements)
|
|
{
|
|
tempStream.WriteValue<int8_t>(mazeElement.location.x);
|
|
tempStream.WriteValue<int8_t>(mazeElement.location.y);
|
|
tempStream.WriteValue<uint16_t>(mazeElement.mazeEntry);
|
|
}
|
|
|
|
for (const auto& entranceElement : _trackDesign.entranceElements)
|
|
{
|
|
tempStream.WriteValue<int8_t>(entranceElement.location.x);
|
|
tempStream.WriteValue<int8_t>(entranceElement.location.y);
|
|
tempStream.WriteValue<int8_t>(entranceElement.location.direction);
|
|
tempStream.WriteValue<int8_t>(
|
|
EnumValue(entranceElement.isExit ? TD46MazeElementType::Exit : TD46MazeElementType::Entrance));
|
|
}
|
|
|
|
tempStream.WriteValue<uint32_t>(0);
|
|
}
|
|
else
|
|
{
|
|
for (const auto& trackElement : _trackDesign.trackElements)
|
|
{
|
|
auto trackType = OpenRCT2TrackTypeToRCT2(trackElement.type);
|
|
if (trackElement.type == TrackElemType::MultiDimInvertedUp90ToFlatQuarterLoop)
|
|
{
|
|
trackType = OpenRCT2::RCT12::TrackElemType::InvertedUp90ToFlatQuarterLoopAlias;
|
|
}
|
|
tempStream.WriteValue<uint8_t>(static_cast<uint8_t>(trackType));
|
|
auto flags = ConvertToTD46Flags(trackElement);
|
|
tempStream.WriteValue<uint8_t>(flags);
|
|
}
|
|
|
|
tempStream.WriteValue<uint8_t>(0xFF);
|
|
|
|
for (const auto& entranceElement : _trackDesign.entranceElements)
|
|
{
|
|
tempStream.WriteValue<uint8_t>(
|
|
entranceElement.location.z == -1 ? static_cast<uint8_t>(0x80) : entranceElement.location.z);
|
|
tempStream.WriteValue<uint8_t>(entranceElement.location.direction | (entranceElement.isExit << 7));
|
|
auto xy = entranceElement.location.ToCoordsXY();
|
|
tempStream.WriteValue<int16_t>(xy.x);
|
|
tempStream.WriteValue<int16_t>(xy.y);
|
|
}
|
|
|
|
tempStream.WriteValue<uint8_t>(0xFF);
|
|
}
|
|
|
|
for (const auto& sceneryElement : _trackDesign.sceneryElements)
|
|
{
|
|
auto flags = sceneryElement.flags;
|
|
if (sceneryElement.sceneryObject.Entry.GetType() == ObjectType::walls)
|
|
{
|
|
flags &= ~0xFC;
|
|
flags |= (sceneryElement.tertiaryColour << 2);
|
|
}
|
|
|
|
tempStream.Write(&sceneryElement.sceneryObject.Entry, sizeof(RCTObjectEntry));
|
|
auto tileCoords = TileCoordsXYZ(sceneryElement.loc);
|
|
tempStream.WriteValue<int8_t>(tileCoords.x);
|
|
tempStream.WriteValue<int8_t>(tileCoords.y);
|
|
tempStream.WriteValue<int8_t>(tileCoords.z);
|
|
tempStream.WriteValue<uint8_t>(flags);
|
|
tempStream.WriteValue<uint8_t>(sceneryElement.primaryColour);
|
|
tempStream.WriteValue<uint8_t>(sceneryElement.secondaryColour);
|
|
}
|
|
|
|
tempStream.WriteValue<uint8_t>(0xFF);
|
|
|
|
SawyerChunkWriter sawyerCoding(stream);
|
|
sawyerCoding.WriteChunkTrack(tempStream.GetData(), tempStream.GetLength());
|
|
return true;
|
|
}
|
|
} // namespace OpenRCT2::RCT2
|