2018-12-30 02:59:49 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
* Copyright (c) 2014-2019 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.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2021-04-02 22:09:34 +02:00
|
|
|
#include "ParkFile.h"
|
|
|
|
|
2020-02-08 12:30:12 +01:00
|
|
|
#include "Cheats.h"
|
2018-12-15 11:34:03 +01:00
|
|
|
#include "Context.h"
|
2021-04-05 19:01:43 +02:00
|
|
|
#include "Editor.h"
|
2018-12-15 14:52:52 +01:00
|
|
|
#include "GameState.h"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "OpenRCT2.h"
|
2018-12-15 13:46:32 +01:00
|
|
|
#include "ParkImporter.h"
|
2018-12-15 01:37:15 +01:00
|
|
|
#include "Version.h"
|
2021-04-07 01:47:16 +02:00
|
|
|
#include "core/Console.hpp"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "core/Crypt.h"
|
2020-02-08 12:30:12 +01:00
|
|
|
#include "core/DataSerialiser.h"
|
2021-04-07 01:47:16 +02:00
|
|
|
#include "core/File.h"
|
2018-12-30 02:59:49 +01:00
|
|
|
#include "core/OrcaStream.hpp"
|
2021-04-07 01:47:16 +02:00
|
|
|
#include "core/Path.hpp"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "drawing/Drawing.h"
|
|
|
|
#include "interface/Viewport.h"
|
|
|
|
#include "interface/Window.h"
|
|
|
|
#include "localisation/Date.h"
|
2018-12-15 16:26:51 +01:00
|
|
|
#include "localisation/Localisation.h"
|
2018-12-16 01:24:35 +01:00
|
|
|
#include "management/Award.h"
|
|
|
|
#include "management/Finance.h"
|
|
|
|
#include "management/NewsItem.h"
|
2018-12-15 15:49:53 +01:00
|
|
|
#include "object/Object.h"
|
2018-12-15 13:46:32 +01:00
|
|
|
#include "object/ObjectManager.h"
|
|
|
|
#include "object/ObjectRepository.h"
|
2020-02-08 10:21:36 +01:00
|
|
|
#include "peep/Staff.h"
|
2018-12-16 01:24:35 +01:00
|
|
|
#include "ride/ShopItem.h"
|
2021-03-27 10:35:02 +01:00
|
|
|
#include "ride/Vehicle.h"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "scenario/Scenario.h"
|
2021-04-03 18:27:56 +02:00
|
|
|
#include "scenario/ScenarioRepository.h"
|
2018-12-16 00:31:30 +01:00
|
|
|
#include "world/Climate.h"
|
2021-03-27 10:35:02 +01:00
|
|
|
#include "world/EntityList.h"
|
2018-12-15 15:31:17 +01:00
|
|
|
#include "world/Entrance.h"
|
2018-12-15 03:20:42 +01:00
|
|
|
#include "world/Map.h"
|
|
|
|
#include "world/Park.h"
|
2021-03-27 10:35:02 +01:00
|
|
|
#include "world/Sprite.h"
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
#include <cstdint>
|
2018-12-16 00:19:36 +01:00
|
|
|
#include <ctime>
|
2018-12-30 00:10:44 +01:00
|
|
|
#include <numeric>
|
2018-12-15 15:49:53 +01:00
|
|
|
#include <string_view>
|
|
|
|
#include <vector>
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2021-04-03 03:17:06 +02:00
|
|
|
using namespace OpenRCT2;
|
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
namespace OpenRCT2
|
|
|
|
{
|
|
|
|
constexpr uint32_t PARK_FILE_MAGIC = 0x4B524150; // PARK
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
// Current version that is saved.
|
2021-05-10 22:37:27 +02:00
|
|
|
constexpr uint32_t PARK_FILE_CURRENT_VERSION = 0x1;
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
// The minimum version that is forwards compatible with the current version.
|
2021-05-10 22:37:27 +02:00
|
|
|
constexpr uint32_t PARK_FILE_MIN_VERSION = 0x1;
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
namespace ParkFileChunkType
|
|
|
|
{
|
|
|
|
// clang-format off
|
2021-03-27 17:08:43 +01:00
|
|
|
// constexpr uint32_t RESERVED_0 = 0x00;
|
2018-12-16 01:24:35 +01:00
|
|
|
|
|
|
|
constexpr uint32_t AUTHORING = 0x01;
|
|
|
|
constexpr uint32_t OBJECTS = 0x02;
|
|
|
|
constexpr uint32_t SCENARIO = 0x03;
|
|
|
|
constexpr uint32_t GENERAL = 0x04;
|
|
|
|
constexpr uint32_t CLIMATE = 0x05;
|
|
|
|
constexpr uint32_t PARK = 0x06;
|
2021-03-27 17:08:43 +01:00
|
|
|
// constexpr uint32_t HISTORY = 0x07;
|
2018-12-16 01:24:35 +01:00
|
|
|
constexpr uint32_t RESEARCH = 0x08;
|
|
|
|
constexpr uint32_t NOTIFICATIONS = 0x09;
|
|
|
|
constexpr uint32_t INTERFACE = 0x20;
|
|
|
|
constexpr uint32_t TILES = 0x30;
|
2021-01-16 13:08:10 +01:00
|
|
|
constexpr uint32_t ENTITIES = 0x31;
|
2018-12-16 01:24:35 +01:00
|
|
|
constexpr uint32_t RIDES = 0x32;
|
|
|
|
constexpr uint32_t BANNERS = 0x33;
|
2021-03-27 17:08:43 +01:00
|
|
|
// constexpr uint32_t STAFF = 0x35;
|
2020-02-08 12:30:12 +01:00
|
|
|
constexpr uint32_t CHEATS = 0x36;
|
2021-04-29 02:07:32 +02:00
|
|
|
constexpr uint32_t RESTRICTED_OBJECTS = 0x37;
|
2021-04-07 01:47:16 +02:00
|
|
|
constexpr uint32_t PACKED_OBJECTS = 0x80;
|
2018-12-15 15:49:53 +01:00
|
|
|
// clang-format on
|
|
|
|
}; // namespace ParkFileChunkType
|
2018-12-15 03:20:42 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
class ParkFile
|
2018-12-15 03:20:42 +01:00
|
|
|
{
|
2018-12-15 15:49:53 +01:00
|
|
|
public:
|
2021-04-01 02:13:31 +02:00
|
|
|
ObjectList RequiredObjects;
|
2021-04-07 01:47:16 +02:00
|
|
|
std::vector<const ObjectRepositoryItem*> ExportObjectsList;
|
2021-04-27 00:54:00 +02:00
|
|
|
bool OmitTracklessRides{};
|
2018-12-15 01:37:15 +01:00
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
private:
|
2018-12-30 03:03:24 +01:00
|
|
|
std::unique_ptr<OrcaStream> _os;
|
2018-12-15 15:49:53 +01:00
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
public:
|
|
|
|
void Load(const std::string_view& path)
|
2018-12-15 11:34:03 +01:00
|
|
|
{
|
2021-04-03 03:17:06 +02:00
|
|
|
FileStream fs(path, FILE_MODE_OPEN);
|
|
|
|
Load(fs);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Load(IStream& stream)
|
|
|
|
{
|
|
|
|
_os = std::make_unique<OrcaStream>(stream, OrcaStream::Mode::READING);
|
2021-04-01 02:13:31 +02:00
|
|
|
RequiredObjects = {};
|
2018-12-30 03:05:29 +01:00
|
|
|
ReadWriteObjectsChunk(*_os);
|
2021-04-07 01:47:16 +02:00
|
|
|
ReadWritePackedObjectsChunk(*_os);
|
2018-12-15 11:34:03 +01:00
|
|
|
}
|
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
void Import()
|
2018-12-15 03:20:42 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
auto& os = *_os;
|
2018-12-30 03:05:29 +01:00
|
|
|
ReadWriteTilesChunk(os);
|
2019-08-18 23:18:22 +02:00
|
|
|
ReadWriteBannersChunk(os);
|
2018-12-31 14:05:44 +01:00
|
|
|
ReadWriteRidesChunk(os);
|
2021-01-16 13:08:10 +01:00
|
|
|
ReadWriteEntitiesChunk(os);
|
2018-12-30 03:05:29 +01:00
|
|
|
ReadWriteScenarioChunk(os);
|
|
|
|
ReadWriteGeneralChunk(os);
|
|
|
|
ReadWriteParkChunk(os);
|
|
|
|
ReadWriteClimateChunk(os);
|
|
|
|
ReadWriteResearchChunk(os);
|
|
|
|
ReadWriteNotificationsChunk(os);
|
|
|
|
ReadWriteInterfaceChunk(os);
|
2020-02-08 12:30:12 +01:00
|
|
|
ReadWriteCheatsChunk(os);
|
2021-04-29 02:07:32 +02:00
|
|
|
ReadWriteRestrictedObjectsChunk(os);
|
2018-12-30 00:10:44 +01:00
|
|
|
|
|
|
|
// Initial cash will eventually be removed
|
|
|
|
gInitialCash = gCash;
|
2018-12-15 03:20:42 +01:00
|
|
|
}
|
|
|
|
|
2021-04-03 03:17:06 +02:00
|
|
|
void Save(IStream& stream)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2021-04-03 03:17:06 +02:00
|
|
|
OrcaStream os(stream, OrcaStream::Mode::WRITING);
|
2018-12-30 02:59:49 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
auto& header = os.GetHeader();
|
2018-12-30 02:59:49 +01:00
|
|
|
header.Magic = PARK_FILE_MAGIC;
|
|
|
|
header.TargetVersion = PARK_FILE_CURRENT_VERSION;
|
|
|
|
header.MinVersion = PARK_FILE_MIN_VERSION;
|
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
ReadWriteAuthoringChunk(os);
|
|
|
|
ReadWriteObjectsChunk(os);
|
|
|
|
ReadWriteTilesChunk(os);
|
2019-08-18 23:18:22 +02:00
|
|
|
ReadWriteBannersChunk(os);
|
2019-01-03 16:32:45 +01:00
|
|
|
ReadWriteRidesChunk(os);
|
2021-01-16 13:08:10 +01:00
|
|
|
ReadWriteEntitiesChunk(os);
|
2018-12-30 03:05:29 +01:00
|
|
|
ReadWriteScenarioChunk(os);
|
|
|
|
ReadWriteGeneralChunk(os);
|
|
|
|
ReadWriteParkChunk(os);
|
|
|
|
ReadWriteClimateChunk(os);
|
|
|
|
ReadWriteResearchChunk(os);
|
|
|
|
ReadWriteNotificationsChunk(os);
|
|
|
|
ReadWriteInterfaceChunk(os);
|
2020-02-08 12:30:12 +01:00
|
|
|
ReadWriteCheatsChunk(os);
|
2021-04-29 02:07:32 +02:00
|
|
|
ReadWriteRestrictedObjectsChunk(os);
|
2021-04-07 01:47:16 +02:00
|
|
|
ReadWritePackedObjectsChunk(os);
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 03:20:42 +01:00
|
|
|
|
2021-04-03 03:17:06 +02:00
|
|
|
void Save(const std::string_view& path)
|
|
|
|
{
|
|
|
|
FileStream fs(path, FILE_MODE_WRITE);
|
|
|
|
Save(fs);
|
|
|
|
}
|
|
|
|
|
2021-04-03 18:27:56 +02:00
|
|
|
scenario_index_entry ReadScenarioChunk()
|
|
|
|
{
|
|
|
|
scenario_index_entry entry{};
|
|
|
|
auto& os = *_os;
|
2021-04-09 23:13:11 +02:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::SCENARIO, [&entry](OrcaStream::ChunkStream& cs) {
|
2021-04-03 18:27:56 +02:00
|
|
|
entry.category = cs.Read<uint8_t>();
|
|
|
|
|
|
|
|
std::string name;
|
|
|
|
ReadWriteStringTable(cs, name, "en-GB");
|
|
|
|
String::Set(entry.name, sizeof(entry.name), name.c_str());
|
|
|
|
String::Set(entry.internal_name, sizeof(entry.internal_name), name.c_str());
|
|
|
|
|
|
|
|
std::string parkName;
|
|
|
|
ReadWriteStringTable(cs, parkName, "en-GB");
|
|
|
|
|
|
|
|
std::string scenarioDetails;
|
|
|
|
ReadWriteStringTable(cs, scenarioDetails, "en-GB");
|
|
|
|
String::Set(entry.details, sizeof(entry.details), scenarioDetails.c_str());
|
|
|
|
|
|
|
|
entry.objective_type = cs.Read<uint8_t>();
|
|
|
|
entry.objective_arg_1 = cs.Read<uint8_t>();
|
|
|
|
entry.objective_arg_3 = cs.Read<int16_t>();
|
|
|
|
entry.objective_arg_2 = cs.Read<int32_t>();
|
|
|
|
|
|
|
|
entry.source_game = ScenarioSource::Other;
|
|
|
|
});
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2018-12-15 15:49:53 +01:00
|
|
|
private:
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteAuthoringChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
// Write-only for now
|
2018-12-30 03:03:24 +01:00
|
|
|
if (os.GetMode() == OrcaStream::Mode::WRITING)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::AUTHORING, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.Write(std::string_view(gVersionInfoFull));
|
2018-12-30 00:10:44 +01:00
|
|
|
std::vector<std::string> authors;
|
2019-01-03 16:32:45 +01:00
|
|
|
cs.ReadWriteVector(authors, [](std::string& s) {});
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.Write(std::string_view()); // custom notes that can be attached to the save
|
|
|
|
cs.Write(static_cast<uint64_t>(std::time(0))); // date started
|
|
|
|
cs.Write(static_cast<uint64_t>(std::time(0))); // date modified
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-15 15:43:24 +01:00
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteObjectsChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2021-03-31 00:26:49 +02:00
|
|
|
static constexpr uint8_t DESCRIPTOR_NONE = 0;
|
|
|
|
static constexpr uint8_t DESCRIPTOR_DAT = 1;
|
|
|
|
static constexpr uint8_t DESCRIPTOR_JSON = 2;
|
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
if (os.GetMode() == OrcaStream::Mode::READING)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2021-04-01 02:13:31 +02:00
|
|
|
ObjectList requiredObjects;
|
2021-03-31 00:26:49 +02:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::OBJECTS, [&requiredObjects](OrcaStream::ChunkStream& cs) {
|
2021-04-01 02:13:31 +02:00
|
|
|
auto numSubLists = cs.Read<uint16_t>();
|
|
|
|
for (size_t i = 0; i < numSubLists; i++)
|
|
|
|
{
|
|
|
|
auto objectType = static_cast<ObjectType>(cs.Read<uint16_t>());
|
|
|
|
auto subListSize = static_cast<ObjectEntryIndex>(cs.Read<uint32_t>());
|
|
|
|
for (ObjectEntryIndex j = 0; j < subListSize; j++)
|
2021-03-31 00:26:49 +02:00
|
|
|
{
|
2021-04-01 02:13:31 +02:00
|
|
|
auto kind = cs.Read<uint8_t>();
|
|
|
|
|
|
|
|
switch (kind)
|
|
|
|
{
|
|
|
|
case DESCRIPTOR_NONE:
|
|
|
|
break;
|
|
|
|
case DESCRIPTOR_DAT:
|
2021-04-06 22:03:01 +02:00
|
|
|
{
|
|
|
|
rct_object_entry datEntry;
|
|
|
|
cs.Read(&datEntry, sizeof(datEntry));
|
|
|
|
ObjectEntryDescriptor desc(datEntry);
|
2021-04-01 02:13:31 +02:00
|
|
|
requiredObjects.SetObject(j, desc);
|
|
|
|
break;
|
2021-04-06 22:03:01 +02:00
|
|
|
}
|
2021-04-01 02:13:31 +02:00
|
|
|
case DESCRIPTOR_JSON:
|
2021-04-06 22:03:01 +02:00
|
|
|
{
|
|
|
|
ObjectEntryDescriptor desc;
|
2021-04-01 02:13:31 +02:00
|
|
|
desc.Type = objectType;
|
|
|
|
desc.Identifier = cs.Read<std::string>();
|
|
|
|
desc.Version = cs.Read<std::string>();
|
|
|
|
requiredObjects.SetObject(j, desc);
|
|
|
|
break;
|
2021-04-06 22:03:01 +02:00
|
|
|
}
|
2021-04-01 02:13:31 +02:00
|
|
|
default:
|
|
|
|
throw std::runtime_error("Unknown object descriptor kind.");
|
|
|
|
}
|
2021-03-31 00:26:49 +02:00
|
|
|
}
|
2021-04-01 02:13:31 +02:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2021-04-01 02:13:31 +02:00
|
|
|
RequiredObjects = std::move(requiredObjects);
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
else
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2021-04-01 02:13:31 +02:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::OBJECTS, [](OrcaStream::ChunkStream& cs) {
|
2018-12-30 00:10:44 +01:00
|
|
|
auto& objManager = GetContext()->GetObjectManager();
|
2021-04-01 02:13:31 +02:00
|
|
|
auto objectList = objManager.GetLoadedObjects();
|
|
|
|
|
|
|
|
// Write number of object sub lists
|
|
|
|
cs.Write(static_cast<uint16_t>(ObjectType::Count));
|
|
|
|
for (auto objectType = ObjectType::Ride; objectType < ObjectType::Count; objectType++)
|
|
|
|
{
|
|
|
|
// Write sub list
|
|
|
|
const auto& list = objectList.GetList(objectType);
|
|
|
|
cs.Write(static_cast<uint16_t>(objectType));
|
|
|
|
cs.Write(static_cast<uint32_t>(list.size()));
|
|
|
|
for (const auto& entry : list)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
2021-04-01 02:13:31 +02:00
|
|
|
if (entry.HasValue())
|
2021-03-31 00:26:49 +02:00
|
|
|
{
|
2021-04-01 02:13:31 +02:00
|
|
|
if (entry.Generation == ObjectGeneration::JSON)
|
|
|
|
{
|
|
|
|
cs.Write(DESCRIPTOR_JSON);
|
|
|
|
cs.Write(entry.Identifier);
|
|
|
|
cs.Write(""); // reserved for version
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.Write(DESCRIPTOR_DAT);
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.Write(&entry.Entry, sizeof(rct_object_entry));
|
2021-04-01 02:13:31 +02:00
|
|
|
}
|
2021-03-31 00:26:49 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-01 02:13:31 +02:00
|
|
|
cs.Write(DESCRIPTOR_NONE);
|
2021-03-31 00:26:49 +02:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
}
|
2021-04-01 02:13:31 +02:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteScenarioChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2021-05-11 21:29:02 +02:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::SCENARIO, [&os](OrcaStream::ChunkStream& cs) {
|
2021-04-05 19:01:43 +02:00
|
|
|
cs.ReadWrite(gScenarioCategory);
|
2018-12-30 03:03:24 +01:00
|
|
|
ReadWriteStringTable(cs, gScenarioName, "en-GB");
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2019-08-01 20:56:38 +02:00
|
|
|
auto& park = GetContext()->GetGameState()->GetPark();
|
|
|
|
ReadWriteStringTable(cs, park.Name, "en-GB");
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
ReadWriteStringTable(cs, gScenarioDetails, "en-GB");
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(gScenarioObjective.Type);
|
|
|
|
cs.ReadWrite(gScenarioObjective.Year);
|
|
|
|
cs.ReadWrite(gScenarioObjective.NumGuests);
|
|
|
|
cs.ReadWrite(gScenarioObjective.Currency);
|
2018-12-15 16:26:51 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gScenarioParkRatingWarningDays);
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gScenarioCompletedCompanyValue);
|
2021-04-10 17:00:50 +02:00
|
|
|
if (gScenarioCompletedCompanyValue == MONEY64_UNDEFINED
|
2020-06-02 22:59:26 +02:00
|
|
|
|| gScenarioCompletedCompanyValue == COMPANY_VALUE_ON_FAILED_OBJECTIVE)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.Write("");
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gScenarioCompletedBy);
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2021-04-03 03:49:15 +02:00
|
|
|
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
auto earlyCompletion = cs.Read<bool>();
|
|
|
|
if (network_get_mode() == NETWORK_MODE_CLIENT)
|
|
|
|
{
|
|
|
|
gAllowEarlyCompletionInNetworkPlay = earlyCompletion;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.Write(AllowEarlyCompletion());
|
|
|
|
}
|
2021-05-11 21:29:02 +02:00
|
|
|
|
|
|
|
if (os.GetHeader().TargetVersion >= 1)
|
|
|
|
{
|
|
|
|
cs.ReadWrite(gScenarioFileName);
|
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 16:26:51 +01:00
|
|
|
}
|
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteGeneralChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2021-04-05 22:58:59 +02:00
|
|
|
auto found = os.ReadWriteChunk(ParkFileChunkType::GENERAL, [this](OrcaStream::ChunkStream& cs) {
|
2021-04-03 03:49:15 +02:00
|
|
|
cs.ReadWrite(gGamePaused);
|
2021-04-06 01:07:13 +02:00
|
|
|
cs.ReadWrite(gCurrentTicks);
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(gDateMonthTicks);
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gDateMonthsElapsed);
|
2019-07-15 22:50:19 +02:00
|
|
|
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
uint32_t s0{}, s1{};
|
|
|
|
cs.ReadWrite(s0);
|
|
|
|
cs.ReadWrite(s1);
|
|
|
|
Random::Rct2::Seed s{ s0, s1 };
|
|
|
|
gScenarioRand.seed(s);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto randState = gScenarioRand.state();
|
|
|
|
cs.Write(randState.s0);
|
|
|
|
cs.Write(randState.s1);
|
|
|
|
}
|
|
|
|
|
2021-04-05 22:58:59 +02:00
|
|
|
cs.ReadWrite(gGuestInitialHappiness);
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gGuestInitialCash);
|
|
|
|
cs.ReadWrite(gGuestInitialHunger);
|
|
|
|
cs.ReadWrite(gGuestInitialThirst);
|
|
|
|
|
|
|
|
cs.ReadWrite(gNextGuestNumber);
|
2019-01-03 16:32:45 +01:00
|
|
|
cs.ReadWriteVector(gPeepSpawns, [&cs](PeepSpawn& spawn) {
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(spawn.x);
|
|
|
|
cs.ReadWrite(spawn.y);
|
|
|
|
cs.ReadWrite(spawn.z);
|
|
|
|
cs.ReadWrite(spawn.direction);
|
2018-12-29 20:43:16 +01:00
|
|
|
});
|
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gLandPrice);
|
|
|
|
cs.ReadWrite(gConstructionRightsPrice);
|
2021-04-08 22:05:34 +02:00
|
|
|
cs.ReadWrite(gGrassSceneryTileLoopPosition);
|
|
|
|
cs.ReadWrite(gWidePathTileLoopPosition);
|
2021-04-05 22:58:59 +02:00
|
|
|
|
|
|
|
ReadWriteRideRatingCalculationData(cs, gRideRatingsCalcData);
|
2018-12-29 20:43:16 +01:00
|
|
|
});
|
|
|
|
if (!found)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-29 20:43:16 +01:00
|
|
|
throw std::runtime_error("No general chunk found.");
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2021-04-05 22:58:59 +02:00
|
|
|
void ReadWriteRideRatingCalculationData(OrcaStream::ChunkStream& cs, RideRatingCalculationData& calcData)
|
|
|
|
{
|
|
|
|
cs.ReadWrite(calcData.AmountOfBrakes);
|
|
|
|
cs.ReadWrite(calcData.Proximity);
|
|
|
|
cs.ReadWrite(calcData.ProximityStart);
|
|
|
|
cs.ReadWrite(calcData.CurrentRide);
|
|
|
|
cs.ReadWrite(calcData.State);
|
|
|
|
cs.ReadWrite(calcData.ProximityTrackType);
|
|
|
|
cs.ReadWrite(calcData.ProximityBaseHeight);
|
|
|
|
cs.ReadWrite(calcData.ProximityTotal);
|
|
|
|
cs.ReadWriteArray(calcData.ProximityScores, [&cs](uint16_t& value) {
|
|
|
|
cs.ReadWrite(value);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
cs.ReadWrite(calcData.AmountOfBrakes);
|
|
|
|
cs.ReadWrite(calcData.AmountOfReversers);
|
|
|
|
cs.ReadWrite(calcData.StationFlags);
|
|
|
|
}
|
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteInterfaceChunk(OrcaStream& os)
|
2018-12-16 01:24:35 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::INTERFACE, [](OrcaStream::ChunkStream& cs) {
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(gSavedView.x);
|
|
|
|
cs.ReadWrite(gSavedView.y);
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
gSavedViewZoom = static_cast<ZoomLevel>(cs.Read<int8_t>());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.Write(static_cast<int8_t>(gSavedViewZoom));
|
|
|
|
}
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gSavedViewRotation);
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(gLastEntranceStyle);
|
2021-04-05 23:28:15 +02:00
|
|
|
cs.ReadWrite(gEditorStep);
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2020-02-08 12:30:12 +01:00
|
|
|
void ReadWriteCheatsChunk(OrcaStream& os)
|
|
|
|
{
|
|
|
|
os.ReadWriteChunk(ParkFileChunkType::CHEATS, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
DataSerialiser ds(cs.GetMode() == OrcaStream::Mode::WRITING, cs.GetStream());
|
|
|
|
CheatsSerialise(ds);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-04-29 02:07:32 +02:00
|
|
|
void ReadWriteRestrictedObjectsChunk(OrcaStream& os)
|
|
|
|
{
|
|
|
|
os.ReadWriteChunk(ParkFileChunkType::RESTRICTED_OBJECTS, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
auto& restrictedScenery = GetRestrictedScenery();
|
|
|
|
|
|
|
|
// We are want to support all object types in the future, so convert scenery type
|
|
|
|
// to object type when we write the list
|
|
|
|
cs.ReadWriteVector(restrictedScenery, [&cs](ScenerySelection& item) {
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
item.SceneryType = GetSceneryTypeFromObjectType(static_cast<ObjectType>(cs.Read<uint16_t>()));
|
|
|
|
item.EntryIndex = cs.Read<ObjectEntryIndex>();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.Write(static_cast<uint16_t>(GetObjectTypeFromSceneryType(item.SceneryType)));
|
|
|
|
cs.Write(item.EntryIndex);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-04-07 01:47:16 +02:00
|
|
|
void ReadWritePackedObjectsChunk(OrcaStream& os)
|
|
|
|
{
|
|
|
|
static constexpr uint8_t DESCRIPTOR_DAT = 0;
|
|
|
|
static constexpr uint8_t DESCRIPTOR_PARKOBJ = 1;
|
|
|
|
|
|
|
|
if (os.GetMode() == OrcaStream::Mode::WRITING && ExportObjectsList.size() == 0)
|
|
|
|
{
|
|
|
|
// Do not emit chunk if there are no packed objects
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
os.ReadWriteChunk(ParkFileChunkType::PACKED_OBJECTS, [this](OrcaStream::ChunkStream& cs) {
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
auto& objRepository = GetContext()->GetObjectRepository();
|
|
|
|
auto numObjects = cs.Read<uint32_t>();
|
|
|
|
for (uint32_t i = 0; i < numObjects; i++)
|
|
|
|
{
|
|
|
|
auto type = cs.Read<uint8_t>();
|
|
|
|
if (type == DESCRIPTOR_DAT)
|
|
|
|
{
|
|
|
|
rct_object_entry entry;
|
|
|
|
cs.Read(&entry, sizeof(entry));
|
|
|
|
auto size = cs.Read<uint32_t>();
|
|
|
|
std::vector<uint8_t> data;
|
|
|
|
data.resize(size);
|
|
|
|
cs.Read(data.data(), data.size());
|
|
|
|
|
|
|
|
auto legacyIdentifier = entry.GetName();
|
|
|
|
if (objRepository.FindObjectLegacy(legacyIdentifier) == nullptr)
|
|
|
|
{
|
2021-04-09 15:59:14 +02:00
|
|
|
objRepository.AddObjectFromFile(
|
|
|
|
ObjectGeneration::DAT, legacyIdentifier, data.data(), data.size());
|
2021-04-07 01:47:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == DESCRIPTOR_PARKOBJ)
|
|
|
|
{
|
|
|
|
auto identifier = cs.Read<std::string>();
|
|
|
|
auto size = cs.Read<uint32_t>();
|
|
|
|
std::vector<uint8_t> data;
|
|
|
|
data.resize(size);
|
|
|
|
cs.Read(data.data(), data.size());
|
|
|
|
if (objRepository.FindObject(identifier) == nullptr)
|
|
|
|
{
|
|
|
|
objRepository.AddObjectFromFile(ObjectGeneration::JSON, identifier, data.data(), data.size());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Unsupported packed object");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto& stream = cs.GetStream();
|
|
|
|
auto countPosition = stream.GetPosition();
|
|
|
|
|
|
|
|
// Write placeholder count, update later
|
|
|
|
uint32_t count = 0;
|
|
|
|
cs.Write(count);
|
|
|
|
|
|
|
|
// Write objects
|
|
|
|
for (const auto* ori : ExportObjectsList)
|
|
|
|
{
|
|
|
|
auto extension = Path::GetExtension(ori->Path);
|
|
|
|
if (String::Equals(extension, ".dat", true))
|
|
|
|
{
|
|
|
|
cs.Write(DESCRIPTOR_DAT);
|
|
|
|
cs.Write(&ori->ObjectEntry, sizeof(rct_object_entry));
|
|
|
|
}
|
|
|
|
else if (String::Equals(extension, ".parkobj", true))
|
|
|
|
{
|
|
|
|
cs.Write(DESCRIPTOR_PARKOBJ);
|
|
|
|
cs.Write(ori->Identifier);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-09 22:51:36 +02:00
|
|
|
Console::WriteLine("%s not packed: unsupported extension.", ori->Identifier.c_str());
|
2021-04-07 01:47:16 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto data = File::ReadAllBytes(ori->Path);
|
2021-04-10 08:23:02 +02:00
|
|
|
cs.Write<uint32_t>(static_cast<uint32_t>(data.size()));
|
2021-04-07 01:47:16 +02:00
|
|
|
cs.Write(data.data(), data.size());
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto backupPosition = stream.GetPosition();
|
|
|
|
stream.SetPosition(countPosition);
|
|
|
|
cs.Write(count);
|
|
|
|
stream.SetPosition(backupPosition);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteClimateChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::CLIMATE, [](OrcaStream::ChunkStream& cs) {
|
|
|
|
cs.ReadWrite(gClimate);
|
|
|
|
cs.ReadWrite(gClimateUpdateTimer);
|
2018-12-15 15:49:53 +01:00
|
|
|
|
2021-04-03 16:10:08 +02:00
|
|
|
for (auto* cl : { &gClimateCurrent, &gClimateNext })
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(cl->Weather);
|
|
|
|
cs.ReadWrite(cl->Temperature);
|
|
|
|
cs.ReadWrite(cl->WeatherEffect);
|
|
|
|
cs.ReadWrite(cl->WeatherGloom);
|
2020-08-28 14:55:21 +02:00
|
|
|
cs.ReadWrite(cl->Level);
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteParkChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::PARK, [](OrcaStream::ChunkStream& cs) {
|
2019-08-01 20:56:38 +02:00
|
|
|
auto& park = GetContext()->GetGameState()->GetPark();
|
|
|
|
cs.ReadWrite(park.Name);
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gCash);
|
|
|
|
cs.ReadWrite(gBankLoan);
|
|
|
|
cs.ReadWrite(gMaxBankLoan);
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(gBankLoanInterestRate);
|
|
|
|
cs.ReadWrite(gParkFlags);
|
|
|
|
cs.ReadWrite(gParkEntranceFee);
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gStaffHandymanColour);
|
|
|
|
cs.ReadWrite(gStaffMechanicColour);
|
|
|
|
cs.ReadWrite(gStaffSecurityColour);
|
2019-07-15 22:50:19 +02:00
|
|
|
cs.ReadWrite(gSamePriceThroughoutPark);
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2021-04-05 22:58:59 +02:00
|
|
|
// Finances
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
auto numMonths = std::min<uint32_t>(EXPENDITURE_TABLE_MONTH_COUNT, cs.Read<uint32_t>());
|
|
|
|
auto numTypes = std::min<uint32_t>(static_cast<uint32_t>(ExpenditureType::Count), cs.Read<uint32_t>());
|
|
|
|
for (uint32_t i = 0; i < numMonths; i++)
|
|
|
|
{
|
|
|
|
for (uint32_t j = 0; j < numTypes; j++)
|
|
|
|
{
|
2021-04-10 17:00:50 +02:00
|
|
|
gExpenditureTable[i][j] = cs.Read<money64>();
|
2021-04-05 22:58:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto numMonths = static_cast<uint32_t>(EXPENDITURE_TABLE_MONTH_COUNT);
|
|
|
|
auto numTypes = static_cast<uint32_t>(ExpenditureType::Count);
|
|
|
|
|
|
|
|
cs.Write(numMonths);
|
|
|
|
cs.Write(numTypes);
|
|
|
|
for (uint32_t i = 0; i < numMonths; i++)
|
|
|
|
{
|
|
|
|
for (uint32_t j = 0; j < numTypes; j++)
|
|
|
|
{
|
|
|
|
cs.Write(gExpenditureTable[i][j]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cs.ReadWrite(gHistoricalProfit);
|
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
// Marketing
|
2019-07-15 22:50:19 +02:00
|
|
|
cs.ReadWriteVector(gMarketingCampaigns, [&cs](MarketingCampaign& campaign) {
|
|
|
|
cs.ReadWrite(campaign.Type);
|
|
|
|
cs.ReadWrite(campaign.WeeksLeft);
|
|
|
|
cs.ReadWrite(campaign.Flags);
|
|
|
|
cs.ReadWrite(campaign.RideId);
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:31:17 +01:00
|
|
|
|
2018-12-30 00:10:44 +01:00
|
|
|
// Awards
|
2019-01-03 16:32:45 +01:00
|
|
|
cs.ReadWriteArray(gCurrentAwards, [&cs](Award& award) {
|
|
|
|
if (award.Time != 0)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
2019-01-03 16:32:45 +01:00
|
|
|
cs.ReadWrite(award.Time);
|
|
|
|
cs.ReadWrite(award.Type);
|
|
|
|
return true;
|
2018-12-30 00:10:44 +01:00
|
|
|
}
|
2019-01-03 16:32:45 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
2018-12-15 15:31:17 +01:00
|
|
|
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(gParkValue);
|
|
|
|
cs.ReadWrite(gCompanyValue);
|
|
|
|
cs.ReadWrite(gParkSize);
|
|
|
|
cs.ReadWrite(gNumGuestsInPark);
|
|
|
|
cs.ReadWrite(gNumGuestsHeadingForPark);
|
|
|
|
cs.ReadWrite(gParkRating);
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gParkRatingCasualtyPenalty);
|
|
|
|
cs.ReadWrite(gCurrentExpenditure);
|
|
|
|
cs.ReadWrite(gCurrentProfit);
|
2021-04-05 22:58:59 +02:00
|
|
|
cs.ReadWrite(gWeeklyProfitAverageDividend);
|
|
|
|
cs.ReadWrite(gWeeklyProfitAverageDivisor);
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gTotalAdmissions);
|
|
|
|
cs.ReadWrite(gTotalIncomeFromAdmissions);
|
2021-04-05 22:58:59 +02:00
|
|
|
cs.ReadWrite(gTotalRideValueForMoney);
|
|
|
|
cs.ReadWrite(gNumGuestsInParkLastWeek);
|
|
|
|
cs.ReadWrite(gGuestChangeModifier);
|
2021-04-03 03:49:15 +02:00
|
|
|
cs.ReadWrite(_guestGenerationProbability);
|
|
|
|
cs.ReadWrite(_suggestedGuestMaximum);
|
2021-04-05 22:58:59 +02:00
|
|
|
|
|
|
|
cs.ReadWriteArray(gPeepWarningThrottle, [&cs](uint8_t& value) {
|
|
|
|
cs.ReadWrite(value);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
cs.ReadWriteArray(gParkRatingHistory, [&cs](uint8_t& value) {
|
|
|
|
cs.ReadWrite(value);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2021-04-25 17:23:59 +02:00
|
|
|
cs.ReadWriteArray(gGuestsInParkHistory, [&cs](uint32_t& value) {
|
2021-04-05 22:58:59 +02:00
|
|
|
cs.ReadWrite(value);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2021-04-10 17:00:50 +02:00
|
|
|
cs.ReadWriteArray(gCashHistory, [&cs](money64& value) {
|
2021-04-05 22:58:59 +02:00
|
|
|
cs.ReadWrite(value);
|
|
|
|
return true;
|
|
|
|
});
|
2021-04-10 17:00:50 +02:00
|
|
|
cs.ReadWriteArray(gWeeklyProfitHistory, [&cs](money64& value) {
|
2021-04-05 22:58:59 +02:00
|
|
|
cs.ReadWrite(value);
|
|
|
|
return true;
|
|
|
|
});
|
2021-04-10 17:00:50 +02:00
|
|
|
cs.ReadWriteArray(gParkValueHistory, [&cs](money64& value) {
|
2021-04-05 22:58:59 +02:00
|
|
|
cs.ReadWrite(value);
|
|
|
|
return true;
|
|
|
|
});
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 15:43:24 +01:00
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteResearchChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::RESEARCH, [](OrcaStream::ChunkStream& cs) {
|
2018-12-30 00:10:44 +01:00
|
|
|
// Research status
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(gResearchFundingLevel);
|
|
|
|
cs.ReadWrite(gResearchPriorities);
|
|
|
|
cs.ReadWrite(gResearchProgressStage);
|
|
|
|
cs.ReadWrite(gResearchProgress);
|
|
|
|
cs.ReadWrite(gResearchExpectedMonth);
|
|
|
|
cs.ReadWrite(gResearchExpectedDay);
|
2021-03-30 22:46:55 +02:00
|
|
|
ReadWriteResearchItem(cs, gResearchLastItem);
|
|
|
|
ReadWriteResearchItem(cs, gResearchNextItem);
|
2018-12-30 00:10:44 +01:00
|
|
|
|
2021-03-30 22:46:55 +02:00
|
|
|
// Invention list
|
|
|
|
cs.ReadWriteVector(gResearchItemsUninvented, [&cs](ResearchItem& item) { ReadWriteResearchItem(cs, item); });
|
|
|
|
cs.ReadWriteVector(gResearchItemsInvented, [&cs](ResearchItem& item) { ReadWriteResearchItem(cs, item); });
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
2018-12-15 14:52:52 +01:00
|
|
|
|
2021-03-30 22:46:55 +02:00
|
|
|
static void ReadWriteResearchItem(OrcaStream::ChunkStream& cs, std::optional<ResearchItem>& item)
|
|
|
|
{
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
auto hasValue = cs.Read<bool>();
|
|
|
|
if (hasValue)
|
|
|
|
{
|
|
|
|
ResearchItem placeholder;
|
|
|
|
ReadWriteResearchItem(cs, placeholder);
|
|
|
|
item = placeholder;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (item)
|
|
|
|
{
|
|
|
|
cs.Write<bool>(true);
|
|
|
|
ReadWriteResearchItem(cs, *item);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.Write<bool>(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ReadWriteResearchItem(OrcaStream::ChunkStream& cs, ResearchItem& item)
|
|
|
|
{
|
|
|
|
cs.ReadWrite(item.type);
|
|
|
|
cs.ReadWrite(item.baseRideType);
|
|
|
|
cs.ReadWrite(item.entryIndex);
|
|
|
|
cs.ReadWrite(item.flags);
|
|
|
|
cs.ReadWrite(item.category);
|
|
|
|
}
|
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteNotificationsChunk(OrcaStream& os)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::NOTIFICATIONS, [](OrcaStream::ChunkStream& cs) {
|
2020-08-28 14:55:21 +02:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
gNewsItems.Clear();
|
|
|
|
|
|
|
|
std::vector<News::Item> recent;
|
|
|
|
cs.ReadWriteVector(recent, [&cs](News::Item& item) { ReadWriteNewsItem(cs, item); });
|
|
|
|
for (size_t i = 0; i < std::min<size_t>(recent.size(), News::ItemHistoryStart); i++)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
2020-08-28 14:55:21 +02:00
|
|
|
gNewsItems[i] = recent[i];
|
2018-12-30 00:10:44 +01:00
|
|
|
}
|
2020-08-28 14:55:21 +02:00
|
|
|
|
|
|
|
std::vector<News::Item> archived;
|
|
|
|
cs.ReadWriteVector(archived, [&cs](News::Item& item) { ReadWriteNewsItem(cs, item); });
|
|
|
|
size_t offset = News::ItemHistoryStart;
|
|
|
|
for (size_t i = 0; i < std::min<size_t>(archived.size(), News::MaxItemsArchive); i++)
|
2018-12-30 00:10:44 +01:00
|
|
|
{
|
2020-08-28 14:55:21 +02:00
|
|
|
gNewsItems[offset + i] = archived[i];
|
2018-12-30 00:10:44 +01:00
|
|
|
}
|
2020-08-28 14:55:21 +02:00
|
|
|
}
|
|
|
|
else
|
2018-12-15 16:26:51 +01:00
|
|
|
{
|
2020-08-28 14:55:21 +02:00
|
|
|
std::vector<News::Item> recent(std::begin(gNewsItems.GetRecent()), std::end(gNewsItems.GetRecent()));
|
|
|
|
cs.ReadWriteVector(recent, [&cs](News::Item& item) { ReadWriteNewsItem(cs, item); });
|
|
|
|
|
|
|
|
std::vector<News::Item> archived(std::begin(gNewsItems.GetArchived()), std::end(gNewsItems.GetArchived()));
|
|
|
|
cs.ReadWriteVector(archived, [&cs](News::Item& item) { ReadWriteNewsItem(cs, item); });
|
2018-12-15 16:26:51 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-15 16:26:51 +01:00
|
|
|
}
|
|
|
|
|
2020-08-28 14:55:21 +02:00
|
|
|
static void ReadWriteNewsItem(OrcaStream::ChunkStream& cs, News::Item& item)
|
|
|
|
{
|
|
|
|
cs.ReadWrite(item.Type);
|
|
|
|
cs.ReadWrite(item.Flags);
|
|
|
|
cs.ReadWrite(item.Assoc);
|
|
|
|
cs.ReadWrite(item.Ticks);
|
|
|
|
cs.ReadWrite(item.MonthYear);
|
|
|
|
cs.ReadWrite(item.Day);
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
auto s = cs.Read<std::string>();
|
2021-03-27 10:35:02 +01:00
|
|
|
item.Text = s;
|
2020-08-28 14:55:21 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-03-27 10:35:02 +01:00
|
|
|
cs.Write(std::string_view(item.Text));
|
2020-08-28 14:55:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-30 03:05:29 +01:00
|
|
|
void ReadWriteTilesChunk(OrcaStream& os)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 03:03:24 +01:00
|
|
|
auto found = os.ReadWriteChunk(ParkFileChunkType::TILES, [](OrcaStream::ChunkStream& cs) {
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(gMapSize); // x
|
|
|
|
cs.Write(gMapSize); // y
|
2018-12-30 00:10:44 +01:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
OpenRCT2::GetContext()->GetGameState()->InitAll(gMapSize);
|
2018-12-15 15:49:53 +01:00
|
|
|
|
2021-04-09 03:09:08 +02:00
|
|
|
auto numElements = cs.Read<uint32_t>();
|
2018-12-15 14:52:52 +01:00
|
|
|
|
2021-04-09 03:09:08 +02:00
|
|
|
std::vector<TileElement> tileElements;
|
|
|
|
tileElements.resize(numElements);
|
|
|
|
cs.Read(tileElements.data(), tileElements.size() * sizeof(TileElement));
|
|
|
|
SetTileElements(std::move(tileElements));
|
|
|
|
UpdateParkEntranceLocations();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-27 01:04:38 +02:00
|
|
|
auto tileElements = GetReorganisedTileElementsWithoutGhosts();
|
2021-04-09 03:09:08 +02:00
|
|
|
cs.Write(static_cast<uint32_t>(tileElements.size()));
|
|
|
|
cs.Write(tileElements.data(), tileElements.size() * sizeof(TileElement));
|
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
|
|
|
if (!found)
|
2018-12-15 15:49:53 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
throw std::runtime_error("No tiles chunk found.");
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
2018-12-15 14:52:52 +01:00
|
|
|
|
2019-08-18 23:18:22 +02:00
|
|
|
void ReadWriteBannersChunk(OrcaStream& os)
|
|
|
|
{
|
2021-05-10 22:37:27 +02:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::BANNERS, [this, &os](OrcaStream::ChunkStream& cs) {
|
|
|
|
auto version = os.GetHeader().TargetVersion;
|
2019-08-18 23:18:22 +02:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::WRITING)
|
|
|
|
{
|
2021-05-10 22:37:27 +02:00
|
|
|
auto numBanners = GetNumBanners();
|
|
|
|
cs.Write(static_cast<uint32_t>(numBanners));
|
|
|
|
|
|
|
|
[[maybe_unused]] size_t numWritten = 0;
|
2019-08-18 23:18:22 +02:00
|
|
|
for (BannerIndex i = 0; i < MAX_BANNERS; i++)
|
|
|
|
{
|
2021-05-10 22:37:27 +02:00
|
|
|
auto banner = GetBanner(i);
|
|
|
|
if (banner != nullptr)
|
|
|
|
{
|
2021-05-11 23:18:02 +02:00
|
|
|
ReadWriteBanner(version, cs, *banner);
|
2021-05-10 22:37:27 +02:00
|
|
|
numWritten++;
|
|
|
|
}
|
2019-08-18 23:18:22 +02:00
|
|
|
}
|
2021-05-10 22:37:27 +02:00
|
|
|
|
|
|
|
assert(numWritten == numBanners);
|
2019-08-18 23:18:22 +02:00
|
|
|
}
|
2021-05-10 22:37:27 +02:00
|
|
|
else if (cs.GetMode() == OrcaStream::Mode::READING)
|
2019-08-18 23:18:22 +02:00
|
|
|
{
|
2021-05-10 22:37:27 +02:00
|
|
|
if (version == 0)
|
2019-08-18 23:18:22 +02:00
|
|
|
{
|
2021-05-11 23:18:02 +02:00
|
|
|
std::vector<Banner> banners;
|
|
|
|
cs.ReadWriteVector(banners, [version, &cs](Banner& banner) { ReadWriteBanner(version, cs, banner); });
|
|
|
|
for (size_t i = 0; i < banners.size(); i++)
|
2021-05-10 22:37:27 +02:00
|
|
|
{
|
|
|
|
auto banner = GetOrCreateBanner(i);
|
2021-05-11 23:18:02 +02:00
|
|
|
if (banner != nullptr)
|
|
|
|
{
|
|
|
|
*banner = std::move(banners[i]);
|
|
|
|
banner->id = static_cast<BannerIndex>(i);
|
|
|
|
}
|
2021-05-10 22:37:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto numBanners = cs.Read<uint32_t>();
|
|
|
|
for (size_t i = 0; i < numBanners; i++)
|
|
|
|
{
|
2021-05-11 23:18:02 +02:00
|
|
|
Banner readBanner;
|
|
|
|
ReadWriteBanner(version, cs, readBanner);
|
|
|
|
|
|
|
|
auto banner = GetOrCreateBanner(readBanner.id);
|
2021-05-10 22:37:27 +02:00
|
|
|
if (banner == nullptr)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Invalid banner index");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-05-11 23:18:02 +02:00
|
|
|
*banner = std::move(readBanner);
|
2021-05-10 22:37:27 +02:00
|
|
|
}
|
|
|
|
}
|
2019-08-18 23:18:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2021-05-10 22:37:27 +02:00
|
|
|
}
|
|
|
|
|
2021-05-11 23:18:02 +02:00
|
|
|
static void ReadWriteBanner(uint32_t version, OrcaStream::ChunkStream& cs, Banner& banner)
|
2021-05-10 22:37:27 +02:00
|
|
|
{
|
2021-05-11 23:18:02 +02:00
|
|
|
if (version >= 1)
|
|
|
|
{
|
|
|
|
cs.ReadWrite(banner.id);
|
|
|
|
}
|
2021-05-10 22:37:27 +02:00
|
|
|
cs.ReadWrite(banner.type);
|
|
|
|
cs.ReadWrite(banner.flags);
|
|
|
|
cs.ReadWrite(banner.text);
|
|
|
|
cs.ReadWrite(banner.colour);
|
|
|
|
cs.ReadWrite(banner.ride_index);
|
|
|
|
cs.ReadWrite(banner.text_colour);
|
|
|
|
cs.ReadWrite(banner.position.x);
|
|
|
|
cs.ReadWrite(banner.position.y);
|
2019-08-18 23:18:22 +02:00
|
|
|
}
|
|
|
|
|
2018-12-31 14:05:44 +01:00
|
|
|
void ReadWriteRidesChunk(OrcaStream& os)
|
|
|
|
{
|
2021-04-27 00:54:00 +02:00
|
|
|
os.ReadWriteChunk(ParkFileChunkType::RIDES, [this](OrcaStream::ChunkStream& cs) {
|
2019-08-18 23:04:47 +02:00
|
|
|
std::vector<ride_id_t> rideIds;
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
ride_init_all();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-04-27 00:54:00 +02:00
|
|
|
if (OmitTracklessRides)
|
2019-01-03 16:32:45 +01:00
|
|
|
{
|
2021-04-27 00:54:00 +02:00
|
|
|
auto tracklessRides = GetTracklessRides();
|
|
|
|
for (const auto& ride : GetRideManager())
|
|
|
|
{
|
|
|
|
auto it = std::find(tracklessRides.begin(), tracklessRides.end(), ride.id);
|
|
|
|
if (it == tracklessRides.end())
|
|
|
|
{
|
|
|
|
rideIds.push_back(ride.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (const auto& ride : GetRideManager())
|
|
|
|
{
|
|
|
|
rideIds.push_back(ride.id);
|
|
|
|
}
|
2019-01-03 16:32:45 +01:00
|
|
|
}
|
2019-08-18 23:04:47 +02:00
|
|
|
}
|
|
|
|
cs.ReadWriteVector(rideIds, [&cs](ride_id_t& rideId) {
|
|
|
|
// Ride ID
|
|
|
|
cs.ReadWrite(rideId);
|
|
|
|
|
|
|
|
auto& ride = *GetOrAllocateRide(rideId);
|
|
|
|
|
|
|
|
// Status
|
|
|
|
cs.ReadWrite(ride.type);
|
2018-12-31 14:05:44 +01:00
|
|
|
cs.ReadWrite(ride.subtype);
|
|
|
|
cs.ReadWrite(ride.mode);
|
|
|
|
cs.ReadWrite(ride.status);
|
|
|
|
cs.ReadWrite(ride.depart_flags);
|
|
|
|
cs.ReadWrite(ride.lifecycle_flags);
|
|
|
|
|
2019-01-03 16:32:45 +01:00
|
|
|
// Meta
|
2019-08-01 20:56:38 +02:00
|
|
|
cs.ReadWrite(ride.custom_name);
|
|
|
|
cs.ReadWrite(ride.default_name_number);
|
2019-01-03 16:32:45 +01:00
|
|
|
|
2021-04-05 23:28:15 +02:00
|
|
|
cs.ReadWriteArray(ride.price, [&cs](money16& price) {
|
|
|
|
cs.ReadWrite(price);
|
|
|
|
return true;
|
|
|
|
});
|
2019-01-03 16:32:45 +01:00
|
|
|
|
2018-12-31 14:05:44 +01:00
|
|
|
// Colours
|
|
|
|
cs.ReadWrite(ride.entrance_style);
|
|
|
|
cs.ReadWrite(ride.colour_scheme_type);
|
2019-01-03 16:32:45 +01:00
|
|
|
cs.ReadWriteArray(ride.track_colour, [&cs](TrackColour& tc) {
|
|
|
|
cs.ReadWrite(tc.main);
|
|
|
|
cs.ReadWrite(tc.additional);
|
|
|
|
cs.ReadWrite(tc.supports);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
cs.ReadWriteArray(ride.vehicle_colours, [&cs](VehicleColour& vc) {
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(vc.Body);
|
|
|
|
cs.ReadWrite(vc.Trim);
|
|
|
|
cs.ReadWrite(vc.Ternary);
|
2019-01-03 16:32:45 +01:00
|
|
|
return true;
|
|
|
|
});
|
2018-12-31 14:05:44 +01:00
|
|
|
|
|
|
|
// Stations
|
|
|
|
cs.ReadWrite(ride.num_stations);
|
2019-07-16 18:44:32 +02:00
|
|
|
cs.ReadWriteArray(ride.stations, [&cs](RideStation& station) {
|
2018-12-31 14:05:44 +01:00
|
|
|
cs.ReadWrite(station.Start);
|
|
|
|
cs.ReadWrite(station.Height);
|
|
|
|
cs.ReadWrite(station.Length);
|
|
|
|
cs.ReadWrite(station.Depart);
|
|
|
|
cs.ReadWrite(station.TrainAtStation);
|
|
|
|
cs.ReadWrite(station.Entrance);
|
|
|
|
cs.ReadWrite(station.Exit);
|
|
|
|
cs.ReadWrite(station.SegmentLength);
|
|
|
|
cs.ReadWrite(station.SegmentTime);
|
|
|
|
cs.ReadWrite(station.QueueTime);
|
|
|
|
cs.ReadWrite(station.QueueLength);
|
|
|
|
cs.ReadWrite(station.LastPeepInQueue);
|
2019-07-16 18:44:32 +02:00
|
|
|
return true;
|
2018-12-31 14:05:44 +01:00
|
|
|
});
|
|
|
|
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(ride.overall_view.x);
|
|
|
|
cs.ReadWrite(ride.overall_view.y);
|
2018-12-31 14:05:44 +01:00
|
|
|
|
|
|
|
// Vehicles
|
|
|
|
cs.ReadWrite(ride.num_vehicles);
|
|
|
|
cs.ReadWrite(ride.num_cars_per_train);
|
|
|
|
cs.ReadWrite(ride.proposed_num_vehicles);
|
|
|
|
cs.ReadWrite(ride.proposed_num_cars_per_train);
|
|
|
|
cs.ReadWrite(ride.max_trains);
|
|
|
|
cs.ReadWrite(ride.min_max_cars_per_train);
|
|
|
|
cs.ReadWrite(ride.min_waiting_time);
|
|
|
|
cs.ReadWrite(ride.max_waiting_time);
|
2019-01-03 16:32:45 +01:00
|
|
|
cs.ReadWriteArray(ride.vehicles, [&cs](uint16_t& v) {
|
|
|
|
cs.ReadWrite(v);
|
|
|
|
return true;
|
|
|
|
});
|
2018-12-31 14:05:44 +01:00
|
|
|
|
|
|
|
// Operation
|
|
|
|
cs.ReadWrite(ride.operation_option);
|
|
|
|
cs.ReadWrite(ride.lift_hill_speed);
|
|
|
|
cs.ReadWrite(ride.num_circuits);
|
|
|
|
|
|
|
|
// Special
|
|
|
|
cs.ReadWrite(ride.boat_hire_return_direction);
|
|
|
|
cs.ReadWrite(ride.boat_hire_return_position);
|
2020-02-07 17:50:54 +01:00
|
|
|
cs.ReadWrite(ride.ChairliftBullwheelLocation[0]);
|
|
|
|
cs.ReadWrite(ride.ChairliftBullwheelLocation[1]);
|
2018-12-31 14:05:44 +01:00
|
|
|
cs.ReadWrite(ride.chairlift_bullwheel_rotation);
|
|
|
|
cs.ReadWrite(ride.slide_in_use);
|
|
|
|
cs.ReadWrite(ride.slide_peep);
|
|
|
|
cs.ReadWrite(ride.slide_peep_t_shirt_colour);
|
|
|
|
cs.ReadWrite(ride.spiral_slide_progress);
|
|
|
|
cs.ReadWrite(ride.race_winner);
|
|
|
|
cs.ReadWrite(ride.cable_lift);
|
2020-02-07 17:50:54 +01:00
|
|
|
cs.ReadWrite(ride.CableLiftLoc);
|
2018-12-31 14:05:44 +01:00
|
|
|
|
|
|
|
// Stats
|
2019-07-15 22:50:19 +02:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
2019-07-15 23:04:59 +02:00
|
|
|
auto hasMeasurement = cs.Read<uint8_t>();
|
|
|
|
if (hasMeasurement != 0)
|
2019-07-15 22:50:19 +02:00
|
|
|
{
|
2019-07-15 23:04:59 +02:00
|
|
|
ride.measurement = std::make_unique<RideMeasurement>();
|
2019-07-15 22:50:19 +02:00
|
|
|
ReadWriteRideMeasurement(cs, *ride.measurement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-07-15 23:04:59 +02:00
|
|
|
if (ride.measurement == nullptr)
|
2019-07-15 22:50:19 +02:00
|
|
|
{
|
2019-07-15 23:04:59 +02:00
|
|
|
cs.Write<uint8_t>(0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.Write<uint8_t>(1);
|
2019-07-15 22:50:19 +02:00
|
|
|
ReadWriteRideMeasurement(cs, *ride.measurement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-31 14:05:44 +01:00
|
|
|
cs.ReadWrite(ride.special_track_elements);
|
|
|
|
cs.ReadWrite(ride.max_speed);
|
|
|
|
cs.ReadWrite(ride.average_speed);
|
|
|
|
cs.ReadWrite(ride.current_test_segment);
|
|
|
|
cs.ReadWrite(ride.average_speed_test_timeout);
|
|
|
|
|
|
|
|
cs.ReadWrite(ride.max_positive_vertical_g);
|
|
|
|
cs.ReadWrite(ride.max_negative_vertical_g);
|
|
|
|
cs.ReadWrite(ride.max_lateral_g);
|
|
|
|
cs.ReadWrite(ride.previous_vertical_g);
|
|
|
|
cs.ReadWrite(ride.previous_lateral_g);
|
|
|
|
|
|
|
|
cs.ReadWrite(ride.testing_flags);
|
2020-02-07 17:50:54 +01:00
|
|
|
cs.ReadWrite(ride.CurTestTrackLocation);
|
2018-12-31 14:05:44 +01:00
|
|
|
|
|
|
|
cs.ReadWrite(ride.turn_count_default);
|
|
|
|
cs.ReadWrite(ride.turn_count_banked);
|
|
|
|
cs.ReadWrite(ride.turn_count_sloped);
|
|
|
|
|
|
|
|
cs.ReadWrite(ride.inversions);
|
|
|
|
cs.ReadWrite(ride.drops);
|
|
|
|
cs.ReadWrite(ride.start_drop_height);
|
|
|
|
cs.ReadWrite(ride.highest_drop_height);
|
|
|
|
cs.ReadWrite(ride.sheltered_length);
|
|
|
|
cs.ReadWrite(ride.var_11C);
|
|
|
|
cs.ReadWrite(ride.num_sheltered_sections);
|
|
|
|
cs.ReadWrite(ride.current_test_station);
|
|
|
|
cs.ReadWrite(ride.num_block_brakes);
|
|
|
|
cs.ReadWrite(ride.total_air_time);
|
|
|
|
|
|
|
|
cs.ReadWrite(ride.excitement);
|
|
|
|
cs.ReadWrite(ride.intensity);
|
|
|
|
cs.ReadWrite(ride.nausea);
|
|
|
|
|
|
|
|
cs.ReadWrite(ride.value);
|
|
|
|
|
|
|
|
cs.ReadWrite(ride.num_riders);
|
|
|
|
cs.ReadWrite(ride.build_date);
|
|
|
|
cs.ReadWrite(ride.upkeep_cost);
|
|
|
|
|
|
|
|
cs.ReadWrite(ride.cur_num_customers);
|
|
|
|
cs.ReadWrite(ride.num_customers_timeout);
|
2021-04-03 16:10:08 +02:00
|
|
|
|
|
|
|
cs.ReadWriteArray(ride.num_customers, [&cs](uint16_t& v) {
|
|
|
|
cs.ReadWrite(v);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2018-12-31 14:05:44 +01:00
|
|
|
cs.ReadWrite(ride.total_customers);
|
|
|
|
cs.ReadWrite(ride.total_profit);
|
|
|
|
cs.ReadWrite(ride.popularity);
|
|
|
|
cs.ReadWrite(ride.popularity_time_out);
|
|
|
|
cs.ReadWrite(ride.popularity_next);
|
|
|
|
cs.ReadWrite(ride.guests_favourite);
|
|
|
|
cs.ReadWrite(ride.no_primary_items_sold);
|
|
|
|
cs.ReadWrite(ride.no_secondary_items_sold);
|
|
|
|
cs.ReadWrite(ride.income_per_hour);
|
|
|
|
cs.ReadWrite(ride.profit);
|
|
|
|
cs.ReadWrite(ride.satisfaction);
|
|
|
|
cs.ReadWrite(ride.satisfaction_time_out);
|
|
|
|
cs.ReadWrite(ride.satisfaction_next);
|
|
|
|
|
|
|
|
// Breakdown
|
|
|
|
cs.ReadWrite(ride.breakdown_reason_pending);
|
|
|
|
cs.ReadWrite(ride.mechanic_status);
|
|
|
|
cs.ReadWrite(ride.mechanic);
|
|
|
|
cs.ReadWrite(ride.inspection_station);
|
|
|
|
cs.ReadWrite(ride.broken_vehicle);
|
|
|
|
cs.ReadWrite(ride.broken_car);
|
|
|
|
cs.ReadWrite(ride.breakdown_reason);
|
|
|
|
cs.ReadWrite(ride.reliability_subvalue);
|
|
|
|
cs.ReadWrite(ride.reliability_percentage);
|
|
|
|
cs.ReadWrite(ride.unreliability_factor);
|
|
|
|
cs.ReadWrite(ride.downtime);
|
|
|
|
cs.ReadWrite(ride.inspection_interval);
|
|
|
|
cs.ReadWrite(ride.last_inspection);
|
2021-04-03 16:10:08 +02:00
|
|
|
|
|
|
|
cs.ReadWriteArray(ride.downtime_history, [&cs](uint8_t& v) {
|
|
|
|
cs.ReadWrite(v);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
2018-12-31 14:05:44 +01:00
|
|
|
cs.ReadWrite(ride.breakdown_sound_modifier);
|
|
|
|
cs.ReadWrite(ride.not_fixed_timeout);
|
|
|
|
cs.ReadWrite(ride.last_crash_type);
|
|
|
|
cs.ReadWrite(ride.connected_message_throttle);
|
|
|
|
|
|
|
|
cs.ReadWrite(ride.vehicle_change_timeout);
|
|
|
|
|
|
|
|
cs.ReadWrite(ride.current_issues);
|
|
|
|
cs.ReadWrite(ride.last_issue_time);
|
|
|
|
|
|
|
|
// Music
|
|
|
|
cs.ReadWrite(ride.music);
|
|
|
|
cs.ReadWrite(ride.music_tune_id);
|
|
|
|
cs.ReadWrite(ride.music_position);
|
2019-01-03 16:32:45 +01:00
|
|
|
return true;
|
2018-12-31 14:05:44 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-07-15 22:50:19 +02:00
|
|
|
static void ReadWriteRideMeasurement(OrcaStream::ChunkStream& cs, RideMeasurement& measurement)
|
|
|
|
{
|
|
|
|
cs.ReadWrite(measurement.flags);
|
|
|
|
cs.ReadWrite(measurement.last_use_tick);
|
|
|
|
cs.ReadWrite(measurement.num_items);
|
|
|
|
cs.ReadWrite(measurement.current_item);
|
|
|
|
cs.ReadWrite(measurement.vehicle_index);
|
|
|
|
cs.ReadWrite(measurement.current_station);
|
|
|
|
for (size_t i = 0; i < measurement.num_items; i++)
|
|
|
|
{
|
|
|
|
cs.ReadWrite(measurement.vertical[i]);
|
|
|
|
cs.ReadWrite(measurement.lateral[i]);
|
|
|
|
cs.ReadWrite(measurement.velocity[i]);
|
|
|
|
cs.ReadWrite(measurement.altitude[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 11:12:12 +01:00
|
|
|
template<typename T> static void ReadWriteEntity(OrcaStream::ChunkStream& cs, T& entity);
|
2020-08-28 19:21:30 +02:00
|
|
|
|
|
|
|
static void ReadWriteEntityCommon(OrcaStream::ChunkStream& cs, SpriteBase& entity)
|
|
|
|
{
|
|
|
|
cs.ReadWrite(entity.sprite_index);
|
2021-03-27 11:12:12 +01:00
|
|
|
cs.ReadWrite(entity.sprite_height_negative);
|
2020-08-28 19:21:30 +02:00
|
|
|
cs.ReadWrite(entity.x);
|
|
|
|
cs.ReadWrite(entity.y);
|
|
|
|
cs.ReadWrite(entity.z);
|
|
|
|
cs.ReadWrite(entity.sprite_width);
|
|
|
|
cs.ReadWrite(entity.sprite_height_positive);
|
|
|
|
cs.ReadWrite(entity.sprite_direction);
|
|
|
|
}
|
|
|
|
|
2021-03-27 11:12:12 +01:00
|
|
|
static void ReadWritePeep(OrcaStream::ChunkStream& cs, Peep& entity)
|
2020-08-28 19:21:30 +02:00
|
|
|
{
|
2021-03-27 11:12:12 +01:00
|
|
|
ReadWriteEntityCommon(cs, entity);
|
2021-04-03 16:10:08 +02:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
auto name = cs.Read<std::string>();
|
|
|
|
entity.SetName(name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.Write(static_cast<const char*>(entity.Name));
|
|
|
|
}
|
2020-08-28 19:21:30 +02:00
|
|
|
cs.ReadWrite(entity.NextLoc);
|
|
|
|
cs.ReadWrite(entity.NextFlags);
|
|
|
|
cs.ReadWrite(entity.OutsideOfPark);
|
|
|
|
cs.ReadWrite(entity.State);
|
|
|
|
cs.ReadWrite(entity.SubState);
|
|
|
|
cs.ReadWrite(entity.SpriteType);
|
|
|
|
cs.ReadWrite(entity.GuestNumRides);
|
|
|
|
cs.ReadWrite(entity.TshirtColour);
|
|
|
|
cs.ReadWrite(entity.TrousersColour);
|
|
|
|
cs.ReadWrite(entity.DestinationX);
|
|
|
|
cs.ReadWrite(entity.DestinationY);
|
|
|
|
cs.ReadWrite(entity.DestinationTolerance);
|
|
|
|
cs.ReadWrite(entity.Var37);
|
|
|
|
cs.ReadWrite(entity.Energy);
|
|
|
|
cs.ReadWrite(entity.EnergyTarget);
|
|
|
|
cs.ReadWrite(entity.Happiness);
|
|
|
|
cs.ReadWrite(entity.HappinessTarget);
|
|
|
|
cs.ReadWrite(entity.Nausea);
|
|
|
|
cs.ReadWrite(entity.NauseaTarget);
|
|
|
|
cs.ReadWrite(entity.Hunger);
|
|
|
|
cs.ReadWrite(entity.Thirst);
|
|
|
|
cs.ReadWrite(entity.Toilet);
|
|
|
|
cs.ReadWrite(entity.Mass);
|
|
|
|
cs.ReadWrite(entity.TimeToConsume);
|
2021-04-03 16:10:08 +02:00
|
|
|
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
entity.Intensity = IntensityRange(cs.Read<uint8_t>());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cs.Write(static_cast<uint8_t>(entity.Intensity));
|
|
|
|
}
|
|
|
|
|
2020-08-28 19:21:30 +02:00
|
|
|
cs.ReadWrite(entity.NauseaTolerance);
|
|
|
|
cs.ReadWrite(entity.WindowInvalidateFlags);
|
|
|
|
cs.ReadWrite(entity.PaidOnDrink);
|
|
|
|
cs.ReadWriteArray(entity.RideTypesBeenOn, [&cs](uint8_t& rideType) {
|
|
|
|
cs.ReadWrite(rideType);
|
|
|
|
return true;
|
|
|
|
});
|
2021-01-08 20:13:32 +01:00
|
|
|
cs.ReadWrite(entity.ItemFlags);
|
2020-08-28 19:21:30 +02:00
|
|
|
cs.ReadWrite(entity.Photo2RideRef);
|
|
|
|
cs.ReadWrite(entity.Photo3RideRef);
|
|
|
|
cs.ReadWrite(entity.Photo4RideRef);
|
|
|
|
cs.ReadWrite(entity.CurrentRide);
|
|
|
|
cs.ReadWrite(entity.CurrentRideStation);
|
|
|
|
cs.ReadWrite(entity.CurrentTrain);
|
|
|
|
cs.ReadWrite(entity.TimeToSitdown);
|
|
|
|
cs.ReadWrite(entity.SpecialSprite);
|
|
|
|
cs.ReadWrite(entity.ActionSpriteType);
|
|
|
|
cs.ReadWrite(entity.NextActionSpriteType);
|
|
|
|
cs.ReadWrite(entity.ActionSpriteImageOffset);
|
|
|
|
cs.ReadWrite(entity.Action);
|
|
|
|
cs.ReadWrite(entity.ActionFrame);
|
|
|
|
cs.ReadWrite(entity.StepProgress);
|
|
|
|
cs.ReadWrite(entity.GuestNextInQueue);
|
|
|
|
cs.ReadWrite(entity.PeepDirection);
|
|
|
|
cs.ReadWrite(entity.InteractionRideIndex);
|
|
|
|
cs.ReadWrite(entity.TimeInQueue);
|
2021-04-03 13:53:02 +02:00
|
|
|
cs.ReadWriteArray(entity.RidesBeenOn, [&cs](ride_id_t& rideId) {
|
2020-08-28 19:21:30 +02:00
|
|
|
cs.ReadWrite(rideId);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
cs.ReadWrite(entity.Id);
|
|
|
|
cs.ReadWrite(entity.CashInPocket);
|
|
|
|
cs.ReadWrite(entity.CashSpent);
|
2020-10-17 21:35:47 +02:00
|
|
|
// Includes HireDate
|
|
|
|
cs.ReadWrite(entity.ParkEntryTime);
|
2020-08-28 19:21:30 +02:00
|
|
|
cs.ReadWrite(entity.RejoinQueueTimeout);
|
|
|
|
cs.ReadWrite(entity.PreviousRide);
|
|
|
|
cs.ReadWrite(entity.PreviousRideTimeOut);
|
2021-04-03 13:53:02 +02:00
|
|
|
cs.ReadWriteArray(entity.Thoughts, [&cs](rct_peep_thought& thought) {
|
|
|
|
cs.ReadWrite(thought.type);
|
|
|
|
cs.ReadWrite(thought.item);
|
|
|
|
cs.ReadWrite(thought.freshness);
|
|
|
|
cs.ReadWrite(thought.fresh_timeout);
|
|
|
|
return true;
|
2020-08-28 19:21:30 +02:00
|
|
|
});
|
|
|
|
cs.ReadWrite(entity.PathCheckOptimisation);
|
|
|
|
cs.ReadWrite(entity.GuestHeadingToRideId);
|
|
|
|
cs.ReadWrite(entity.GuestIsLostCountdown);
|
|
|
|
cs.ReadWrite(entity.Photo1RideRef);
|
|
|
|
cs.ReadWrite(entity.PeepFlags);
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(entity.PathfindGoal.x);
|
|
|
|
cs.ReadWrite(entity.PathfindGoal.y);
|
|
|
|
cs.ReadWrite(entity.PathfindGoal.z);
|
|
|
|
cs.ReadWrite(entity.PathfindGoal.direction);
|
2020-08-28 19:21:30 +02:00
|
|
|
for (size_t i = 0; i < std::size(entity.PathfindHistory); i++)
|
|
|
|
{
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(entity.PathfindHistory[i].x);
|
|
|
|
cs.ReadWrite(entity.PathfindHistory[i].y);
|
|
|
|
cs.ReadWrite(entity.PathfindHistory[i].z);
|
|
|
|
cs.ReadWrite(entity.PathfindHistory[i].direction);
|
2020-08-28 19:21:30 +02:00
|
|
|
}
|
|
|
|
cs.ReadWrite(entity.WalkingFrameNum);
|
|
|
|
cs.ReadWrite(entity.LitterCount);
|
|
|
|
cs.ReadWrite(entity.GuestTimeOnRide);
|
|
|
|
cs.ReadWrite(entity.DisgustingCount);
|
|
|
|
cs.ReadWrite(entity.PaidToEnter);
|
|
|
|
cs.ReadWrite(entity.PaidOnRides);
|
|
|
|
cs.ReadWrite(entity.PaidOnFood);
|
|
|
|
cs.ReadWrite(entity.PaidOnSouvenirs);
|
|
|
|
cs.ReadWrite(entity.AmountOfFood);
|
|
|
|
cs.ReadWrite(entity.AmountOfDrinks);
|
|
|
|
cs.ReadWrite(entity.AmountOfSouvenirs);
|
|
|
|
cs.ReadWrite(entity.VandalismSeen);
|
|
|
|
cs.ReadWrite(entity.VoucherType);
|
|
|
|
cs.ReadWrite(entity.VoucherRideId);
|
|
|
|
cs.ReadWrite(entity.SurroundingsThoughtTimeout);
|
|
|
|
cs.ReadWrite(entity.Angriness);
|
|
|
|
cs.ReadWrite(entity.TimeLost);
|
|
|
|
cs.ReadWrite(entity.DaysInQueue);
|
|
|
|
cs.ReadWrite(entity.BalloonColour);
|
|
|
|
cs.ReadWrite(entity.UmbrellaColour);
|
|
|
|
cs.ReadWrite(entity.HatColour);
|
|
|
|
cs.ReadWrite(entity.FavouriteRide);
|
|
|
|
cs.ReadWrite(entity.FavouriteRideRating);
|
|
|
|
}
|
|
|
|
|
2021-03-27 16:59:55 +01:00
|
|
|
template<typename T> void WriteEntitiesOfType(OrcaStream::ChunkStream& cs);
|
|
|
|
template<typename... T> void WriteEntitiesOfTypes(OrcaStream::ChunkStream& cs);
|
2021-03-27 11:12:12 +01:00
|
|
|
|
2021-03-27 16:59:55 +01:00
|
|
|
template<typename T> void ReadEntitiesOfType(OrcaStream::ChunkStream& cs);
|
2021-03-27 11:12:12 +01:00
|
|
|
|
2021-03-27 16:59:55 +01:00
|
|
|
template<typename... T> void ReadEntitiesOfTypes(OrcaStream::ChunkStream& cs);
|
2021-03-27 11:12:12 +01:00
|
|
|
|
2021-03-27 16:59:55 +01:00
|
|
|
void ReadWriteEntitiesChunk(OrcaStream& os);
|
2020-08-28 19:21:30 +02:00
|
|
|
|
2018-12-30 03:03:24 +01:00
|
|
|
static void ReadWriteStringTable(OrcaStream::ChunkStream& cs, std::string& value, const std::string_view& lcode)
|
2018-12-16 00:31:30 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
std::vector<std::tuple<std::string, std::string>> table;
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() != OrcaStream::Mode::READING)
|
2018-12-16 00:31:30 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
table.push_back(std::make_tuple(std::string(lcode), value));
|
2018-12-16 00:31:30 +01:00
|
|
|
}
|
2019-01-03 16:32:45 +01:00
|
|
|
cs.ReadWriteVector(table, [&cs](std::tuple<std::string, std::string>& v) {
|
2018-12-30 03:03:24 +01:00
|
|
|
cs.ReadWrite(std::get<0>(v));
|
|
|
|
cs.ReadWrite(std::get<1>(v));
|
2018-12-30 00:10:44 +01:00
|
|
|
});
|
2018-12-30 03:03:24 +01:00
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
2018-12-17 00:24:04 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
auto fr = std::find_if(table.begin(), table.end(), [&lcode](const std::tuple<std::string, std::string>& v) {
|
|
|
|
return std::get<0>(v) == lcode;
|
|
|
|
});
|
|
|
|
if (fr != table.end())
|
2018-12-17 00:24:04 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
value = std::get<1>(*fr);
|
2018-12-17 00:24:04 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
else if (table.size() > 0)
|
2018-12-17 00:24:04 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
value = std::get<1>(table[0]);
|
2018-12-17 00:24:04 +01:00
|
|
|
}
|
2018-12-30 00:10:44 +01:00
|
|
|
else
|
2018-12-17 00:24:04 +01:00
|
|
|
{
|
2018-12-30 00:10:44 +01:00
|
|
|
value = "";
|
2018-12-17 00:24:04 +01:00
|
|
|
}
|
2018-12-15 15:49:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2021-03-27 16:59:55 +01:00
|
|
|
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Vehicle& entity)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, entity);
|
|
|
|
cs.ReadWrite(entity.SubType);
|
|
|
|
cs.ReadWrite(entity.vehicle_sprite_type);
|
|
|
|
cs.ReadWrite(entity.bank_rotation);
|
|
|
|
cs.ReadWrite(entity.remaining_distance);
|
|
|
|
cs.ReadWrite(entity.velocity);
|
|
|
|
cs.ReadWrite(entity.acceleration);
|
|
|
|
cs.ReadWrite(entity.ride);
|
|
|
|
cs.ReadWrite(entity.vehicle_type);
|
2021-04-03 16:10:08 +02:00
|
|
|
cs.ReadWrite(entity.colours.body_colour);
|
|
|
|
cs.ReadWrite(entity.colours.trim_colour);
|
2021-03-27 16:59:55 +01:00
|
|
|
cs.ReadWrite(entity.track_progress);
|
2021-03-30 22:26:02 +02:00
|
|
|
cs.ReadWrite(entity.BoatLocation);
|
|
|
|
cs.ReadWrite(entity.TrackTypeAndDirection);
|
2021-03-27 16:59:55 +01:00
|
|
|
cs.ReadWrite(entity.TrackLocation.x);
|
|
|
|
cs.ReadWrite(entity.TrackLocation.y);
|
|
|
|
cs.ReadWrite(entity.TrackLocation.z);
|
|
|
|
cs.ReadWrite(entity.next_vehicle_on_train);
|
|
|
|
cs.ReadWrite(entity.prev_vehicle_on_ride);
|
|
|
|
cs.ReadWrite(entity.next_vehicle_on_ride);
|
|
|
|
cs.ReadWrite(entity.var_44);
|
|
|
|
cs.ReadWrite(entity.mass);
|
|
|
|
cs.ReadWrite(entity.update_flags);
|
|
|
|
cs.ReadWrite(entity.SwingSprite);
|
|
|
|
cs.ReadWrite(entity.current_station);
|
|
|
|
cs.ReadWrite(entity.current_time);
|
|
|
|
cs.ReadWrite(entity.crash_z);
|
|
|
|
cs.ReadWrite(entity.status);
|
|
|
|
cs.ReadWrite(entity.sub_state);
|
|
|
|
for (size_t i = 0; i < std::size(entity.peep); i++)
|
|
|
|
{
|
|
|
|
cs.ReadWrite(entity.peep[i]);
|
|
|
|
cs.ReadWrite(entity.peep_tshirt_colours[i]);
|
|
|
|
}
|
|
|
|
cs.ReadWrite(entity.num_seats);
|
|
|
|
cs.ReadWrite(entity.num_peeps);
|
|
|
|
cs.ReadWrite(entity.next_free_seat);
|
|
|
|
cs.ReadWrite(entity.restraints_position);
|
|
|
|
cs.ReadWrite(entity.crash_x);
|
|
|
|
cs.ReadWrite(entity.sound2_flags);
|
|
|
|
cs.ReadWrite(entity.spin_sprite);
|
|
|
|
cs.ReadWrite(entity.sound1_id);
|
|
|
|
cs.ReadWrite(entity.sound1_volume);
|
|
|
|
cs.ReadWrite(entity.sound2_id);
|
|
|
|
cs.ReadWrite(entity.sound2_volume);
|
|
|
|
cs.ReadWrite(entity.sound_vector_factor);
|
|
|
|
cs.ReadWrite(entity.time_waiting);
|
|
|
|
cs.ReadWrite(entity.speed);
|
|
|
|
cs.ReadWrite(entity.powered_acceleration);
|
|
|
|
cs.ReadWrite(entity.dodgems_collision_direction);
|
|
|
|
cs.ReadWrite(entity.animation_frame);
|
|
|
|
cs.ReadWrite(entity.var_C8);
|
|
|
|
cs.ReadWrite(entity.var_CA);
|
|
|
|
cs.ReadWrite(entity.scream_sound_id);
|
|
|
|
cs.ReadWrite(entity.TrackSubposition);
|
|
|
|
cs.ReadWrite(entity.var_CE);
|
|
|
|
cs.ReadWrite(entity.var_CF);
|
|
|
|
cs.ReadWrite(entity.lost_time_out);
|
|
|
|
cs.ReadWrite(entity.vertical_drop_countdown);
|
|
|
|
cs.ReadWrite(entity.var_D3);
|
|
|
|
cs.ReadWrite(entity.mini_golf_current_animation);
|
|
|
|
cs.ReadWrite(entity.mini_golf_flags);
|
|
|
|
cs.ReadWrite(entity.ride_subtype);
|
|
|
|
cs.ReadWrite(entity.colours_extended);
|
|
|
|
cs.ReadWrite(entity.seat_rotation);
|
|
|
|
cs.ReadWrite(entity.target_seat_rotation);
|
2021-04-25 14:36:33 +02:00
|
|
|
cs.ReadWrite(entity.IsCrashedVehicle);
|
2021-03-27 16:59:55 +01:00
|
|
|
}
|
|
|
|
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Guest& entity)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWritePeep(cs, entity);
|
|
|
|
}
|
2021-04-06 00:57:35 +02:00
|
|
|
|
2021-05-11 23:38:03 +02:00
|
|
|
static std::vector<TileCoordsXY> GetPatrolArea(Staff& staff)
|
2021-04-06 00:57:35 +02:00
|
|
|
{
|
|
|
|
std::vector<TileCoordsXY> area;
|
2021-05-11 23:38:03 +02:00
|
|
|
if (staff.PatrolInfo != nullptr)
|
2021-04-06 00:57:35 +02:00
|
|
|
{
|
2021-05-11 23:38:03 +02:00
|
|
|
for (size_t i = 0; i < STAFF_PATROL_AREA_SIZE; i++)
|
2021-04-06 00:57:35 +02:00
|
|
|
{
|
|
|
|
// 32 blocks per array item (32 bits)
|
2021-05-11 23:38:03 +02:00
|
|
|
auto arrayItem = staff.PatrolInfo->Data[i];
|
|
|
|
for (size_t j = 0; j < 32; j++)
|
2021-04-06 00:57:35 +02:00
|
|
|
{
|
2021-05-11 23:38:03 +02:00
|
|
|
auto blockIndex = (i * 32) + j;
|
2021-04-06 00:57:35 +02:00
|
|
|
if (arrayItem & (1 << j))
|
|
|
|
{
|
2021-05-11 23:38:03 +02:00
|
|
|
auto sx = (blockIndex % STAFF_PATROL_AREA_BLOCKS_PER_LINE) * 4;
|
|
|
|
auto sy = (blockIndex / STAFF_PATROL_AREA_BLOCKS_PER_LINE) * 4;
|
|
|
|
for (size_t y = 0; y < 4; y++)
|
2021-04-06 00:57:35 +02:00
|
|
|
{
|
2021-05-11 23:38:03 +02:00
|
|
|
for (size_t x = 0; x < 4; x++)
|
2021-04-06 00:57:35 +02:00
|
|
|
{
|
2021-05-11 23:38:03 +02:00
|
|
|
area.push_back({ static_cast<int32_t>(sx + x), static_cast<int32_t>(sy + y) });
|
2021-04-06 00:57:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return area;
|
|
|
|
}
|
|
|
|
|
2021-05-11 23:38:03 +02:00
|
|
|
static void SetPatrolArea(Staff& staff, const std::vector<TileCoordsXY>& area)
|
2021-04-06 00:57:35 +02:00
|
|
|
{
|
|
|
|
if (area.empty())
|
|
|
|
{
|
2021-05-11 23:38:03 +02:00
|
|
|
staff.ClearPatrolArea();
|
2021-04-06 00:57:35 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (const auto& coord : area)
|
|
|
|
{
|
2021-05-11 23:38:03 +02:00
|
|
|
staff.SetPatrolArea(coord.ToCoordsXY(), true);
|
2021-04-06 00:57:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Staff& entity)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWritePeep(cs, entity);
|
2021-04-06 00:57:35 +02:00
|
|
|
|
|
|
|
std::vector<TileCoordsXY> patrolArea;
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::WRITING)
|
|
|
|
{
|
2021-05-11 23:38:03 +02:00
|
|
|
patrolArea = GetPatrolArea(entity);
|
2021-04-06 00:57:35 +02:00
|
|
|
}
|
|
|
|
cs.ReadWriteVector(patrolArea, [&cs](TileCoordsXY& value) { cs.ReadWrite(value); });
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
2021-05-11 23:38:03 +02:00
|
|
|
SetPatrolArea(entity, patrolArea);
|
2021-04-06 00:57:35 +02:00
|
|
|
}
|
2021-03-27 16:59:55 +01:00
|
|
|
}
|
|
|
|
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, SteamParticle& steamParticle)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, steamParticle);
|
|
|
|
cs.ReadWrite(steamParticle.time_to_move);
|
|
|
|
cs.ReadWrite(steamParticle.frame);
|
|
|
|
}
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, MoneyEffect& moneyEffect)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, moneyEffect);
|
|
|
|
cs.ReadWrite(moneyEffect.MoveDelay);
|
|
|
|
cs.ReadWrite(moneyEffect.NumMovements);
|
|
|
|
cs.ReadWrite(moneyEffect.Vertical);
|
|
|
|
cs.ReadWrite(moneyEffect.Value);
|
|
|
|
cs.ReadWrite(moneyEffect.OffsetX);
|
|
|
|
cs.ReadWrite(moneyEffect.Wiggle);
|
|
|
|
}
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, VehicleCrashParticle& vehicleCrashParticle)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, vehicleCrashParticle);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.frame);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.time_to_live);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.frame);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.colour[0]);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.colour[1]);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.crashed_sprite_base);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.velocity_x);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.velocity_y);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.velocity_z);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.acceleration_x);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.acceleration_y);
|
|
|
|
cs.ReadWrite(vehicleCrashParticle.acceleration_z);
|
|
|
|
}
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, ExplosionCloud& entity)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, entity);
|
|
|
|
cs.ReadWrite(entity.frame);
|
|
|
|
}
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, CrashSplashParticle& entity)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, entity);
|
|
|
|
cs.ReadWrite(entity.frame);
|
|
|
|
}
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, ExplosionFlare& entity)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, entity);
|
|
|
|
cs.ReadWrite(entity.frame);
|
|
|
|
}
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, JumpingFountain& fountain)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, fountain);
|
|
|
|
cs.ReadWrite(fountain.NumTicksAlive);
|
|
|
|
cs.ReadWrite(fountain.frame);
|
|
|
|
cs.ReadWrite(fountain.FountainFlags);
|
|
|
|
cs.ReadWrite(fountain.TargetX);
|
|
|
|
cs.ReadWrite(fountain.TargetY);
|
|
|
|
cs.ReadWrite(fountain.TargetY);
|
|
|
|
cs.ReadWrite(fountain.Iteration);
|
|
|
|
}
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Balloon& balloon)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, balloon);
|
|
|
|
cs.ReadWrite(balloon.popped);
|
|
|
|
cs.ReadWrite(balloon.time_to_move);
|
|
|
|
cs.ReadWrite(balloon.frame);
|
|
|
|
cs.ReadWrite(balloon.colour);
|
|
|
|
}
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Duck& duck)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, duck);
|
|
|
|
cs.ReadWrite(duck.frame);
|
|
|
|
cs.ReadWrite(duck.target_x);
|
|
|
|
cs.ReadWrite(duck.target_y);
|
|
|
|
cs.ReadWrite(duck.state);
|
|
|
|
}
|
|
|
|
|
2021-03-27 17:04:47 +01:00
|
|
|
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Litter& entity)
|
2021-03-27 16:59:55 +01:00
|
|
|
{
|
|
|
|
ReadWriteEntityCommon(cs, entity);
|
|
|
|
cs.ReadWrite(entity.SubType);
|
|
|
|
cs.ReadWrite(entity.creationTick);
|
|
|
|
}
|
|
|
|
template<typename T> void ParkFile::WriteEntitiesOfType(OrcaStream::ChunkStream& cs)
|
|
|
|
{
|
|
|
|
uint16_t count = GetEntityListCount(T::cEntityType);
|
|
|
|
cs.Write(T::cEntityType);
|
|
|
|
cs.Write(count);
|
|
|
|
for (auto* ent : EntityList<T>())
|
|
|
|
{
|
|
|
|
cs.Write(ent->sprite_index);
|
|
|
|
ReadWriteEntity(cs, *ent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename... T> void ParkFile::WriteEntitiesOfTypes(OrcaStream::ChunkStream& cs)
|
|
|
|
{
|
|
|
|
(WriteEntitiesOfType<T>(cs), ...);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T> void ParkFile::ReadEntitiesOfType(OrcaStream::ChunkStream& cs)
|
|
|
|
{
|
2021-03-29 20:40:36 +02:00
|
|
|
[[maybe_unused]] auto t = cs.Read<EntityType>();
|
2021-03-27 16:59:55 +01:00
|
|
|
assert(t == T::cEntityType);
|
|
|
|
auto count = cs.Read<uint16_t>();
|
|
|
|
for (auto i = 0; i < count; ++i)
|
|
|
|
{
|
2021-03-30 22:26:02 +02:00
|
|
|
T placeholder{};
|
|
|
|
|
2021-03-27 16:59:55 +01:00
|
|
|
auto index = cs.Read<uint16_t>();
|
|
|
|
auto* ent = CreateEntityAt<T>(index);
|
2021-03-30 22:26:02 +02:00
|
|
|
if (ent == nullptr)
|
|
|
|
{
|
|
|
|
// Unable to allocate entity
|
|
|
|
ent = &placeholder;
|
|
|
|
}
|
2021-03-27 16:59:55 +01:00
|
|
|
ReadWriteEntity(cs, *ent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename... T> void ParkFile::ReadEntitiesOfTypes(OrcaStream::ChunkStream& cs)
|
|
|
|
{
|
|
|
|
(ReadEntitiesOfType<T>(cs), ...);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ParkFile::ReadWriteEntitiesChunk(OrcaStream& os)
|
|
|
|
{
|
|
|
|
os.ReadWriteChunk(ParkFileChunkType::ENTITIES, [this](OrcaStream::ChunkStream& cs) {
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
reset_sprite_list();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<uint16_t> entityIndices;
|
|
|
|
if (cs.GetMode() == OrcaStream::Mode::READING)
|
|
|
|
{
|
|
|
|
ReadEntitiesOfTypes<
|
|
|
|
Vehicle, Guest, Staff, Litter, SteamParticle, MoneyEffect, VehicleCrashParticle, ExplosionCloud,
|
|
|
|
CrashSplashParticle, ExplosionFlare, JumpingFountain, Balloon, Duck>(cs);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WriteEntitiesOfTypes<
|
|
|
|
Vehicle, Guest, Staff, Litter, SteamParticle, MoneyEffect, VehicleCrashParticle, ExplosionCloud,
|
|
|
|
CrashSplashParticle, ExplosionFlare, JumpingFountain, Balloon, Duck>(cs);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2018-12-15 15:49:53 +01:00
|
|
|
} // namespace OpenRCT2
|
2018-12-15 14:52:52 +01:00
|
|
|
|
2021-04-02 22:09:34 +02:00
|
|
|
void ParkFileExporter::Export(std::string_view path)
|
|
|
|
{
|
|
|
|
auto parkFile = std::make_unique<OpenRCT2::ParkFile>();
|
|
|
|
parkFile->Save(path);
|
|
|
|
}
|
|
|
|
|
2021-04-03 03:17:06 +02:00
|
|
|
void ParkFileExporter::Export(IStream& stream)
|
|
|
|
{
|
|
|
|
auto parkFile = std::make_unique<OpenRCT2::ParkFile>();
|
2021-04-08 21:36:26 +02:00
|
|
|
parkFile->ExportObjectsList = ExportObjectsList;
|
2021-04-03 03:17:06 +02:00
|
|
|
parkFile->Save(stream);
|
|
|
|
}
|
|
|
|
|
2018-12-15 03:20:42 +01:00
|
|
|
enum : uint32_t
|
|
|
|
{
|
|
|
|
S6_SAVE_FLAG_EXPORT = 1 << 0,
|
|
|
|
S6_SAVE_FLAG_SCENARIO = 1 << 1,
|
|
|
|
S6_SAVE_FLAG_AUTOMATIC = 1u << 31,
|
|
|
|
};
|
|
|
|
|
|
|
|
int32_t scenario_save(const utf8* path, int32_t flags)
|
|
|
|
{
|
|
|
|
if (flags & S6_SAVE_FLAG_SCENARIO)
|
|
|
|
{
|
|
|
|
log_verbose("saving scenario");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
log_verbose("saving game");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(flags & S6_SAVE_FLAG_AUTOMATIC))
|
|
|
|
{
|
|
|
|
window_close_construction_windows();
|
|
|
|
}
|
|
|
|
|
|
|
|
viewport_set_saved_view();
|
|
|
|
|
|
|
|
bool result = false;
|
2018-12-15 15:49:53 +01:00
|
|
|
auto parkFile = std::make_unique<OpenRCT2::ParkFile>();
|
2018-12-15 03:20:42 +01:00
|
|
|
try
|
|
|
|
{
|
2021-04-07 01:47:16 +02:00
|
|
|
if (flags & S6_SAVE_FLAG_EXPORT)
|
|
|
|
{
|
|
|
|
auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
|
|
|
|
parkFile->ExportObjectsList = objManager.GetPackableObjects();
|
|
|
|
}
|
2021-04-27 00:54:00 +02:00
|
|
|
parkFile->OmitTracklessRides = true;
|
2018-12-15 03:20:42 +01:00
|
|
|
if (flags & S6_SAVE_FLAG_SCENARIO)
|
|
|
|
{
|
|
|
|
// s6exporter->SaveScenario(path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// s6exporter->SaveGame(path);
|
|
|
|
}
|
|
|
|
parkFile->Save(path);
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
catch (const std::exception&)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
gfx_invalidate_screen();
|
|
|
|
|
|
|
|
if (result && !(flags & S6_SAVE_FLAG_AUTOMATIC))
|
2018-12-15 01:37:15 +01:00
|
|
|
{
|
2018-12-15 03:20:42 +01:00
|
|
|
gScreenAge = 0;
|
2018-12-15 01:37:15 +01:00
|
|
|
}
|
2018-12-15 03:20:42 +01:00
|
|
|
return result;
|
2018-12-15 01:37:15 +01:00
|
|
|
}
|
2018-12-15 13:46:32 +01:00
|
|
|
|
2021-04-10 08:47:17 +02:00
|
|
|
class ParkFileImporter final : public IParkImporter
|
2018-12-15 13:46:32 +01:00
|
|
|
{
|
|
|
|
private:
|
2019-10-27 18:19:35 +01:00
|
|
|
#ifdef __clang__
|
|
|
|
[[maybe_unused]]
|
|
|
|
#endif
|
|
|
|
const IObjectRepository& _objectRepository;
|
2018-12-15 15:49:53 +01:00
|
|
|
std::unique_ptr<OpenRCT2::ParkFile> _parkFile;
|
2018-12-15 13:46:32 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
ParkFileImporter(IObjectRepository& objectRepository)
|
|
|
|
: _objectRepository(objectRepository)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ParkLoadResult Load(const utf8* path) override
|
|
|
|
{
|
2018-12-15 15:49:53 +01:00
|
|
|
_parkFile = std::make_unique<OpenRCT2::ParkFile>();
|
2018-12-15 14:52:52 +01:00
|
|
|
_parkFile->Load(path);
|
|
|
|
return ParkLoadResult(std::move(_parkFile->RequiredObjects));
|
2018-12-15 13:46:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ParkLoadResult LoadSavedGame(const utf8* path, bool skipObjectCheck = false) override
|
|
|
|
{
|
|
|
|
return Load(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
ParkLoadResult LoadScenario(const utf8* path, bool skipObjectCheck = false) override
|
|
|
|
{
|
|
|
|
return Load(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
ParkLoadResult LoadFromStream(
|
2020-08-28 14:55:21 +02:00
|
|
|
OpenRCT2::IStream* stream, bool isScenario, bool skipObjectCheck = false, const utf8* path = String::Empty) override
|
2018-12-15 13:46:32 +01:00
|
|
|
{
|
2021-04-03 03:17:06 +02:00
|
|
|
_parkFile = std::make_unique<OpenRCT2::ParkFile>();
|
|
|
|
_parkFile->Load(*stream);
|
|
|
|
return ParkLoadResult(std::move(_parkFile->RequiredObjects));
|
2018-12-15 13:46:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Import() override
|
|
|
|
{
|
2018-12-15 14:52:52 +01:00
|
|
|
_parkFile->Import();
|
2021-04-08 01:49:04 +02:00
|
|
|
game_fix_save_vars();
|
2018-12-15 13:46:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool GetDetails(scenario_index_entry* dst) override
|
|
|
|
{
|
2021-04-03 18:27:56 +02:00
|
|
|
*dst = _parkFile->ReadScenarioChunk();
|
|
|
|
return true;
|
2018-12-15 13:46:32 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
std::unique_ptr<IParkImporter> ParkImporter::CreateParkFile(IObjectRepository& objectRepository)
|
|
|
|
{
|
|
|
|
return std::make_unique<ParkFileImporter>(objectRepository);
|
|
|
|
}
|