OpenRCT2/src/openrct2/ParkFile.cpp

1372 lines
53 KiB
C++
Raw Normal View History

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"
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"
2018-12-15 03:20:42 +01:00
#include "core/Crypt.h"
2020-02-08 12:30:12 +01:00
#include "core/DataSerialiser.h"
2018-12-30 02:59:49 +01:00
#include "core/OrcaStream.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"
#include "object/Object.h"
2018-12-15 13:46:32 +01:00
#include "object/ObjectManager.h"
#include "object/ObjectRepository.h"
#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"
2018-12-16 00:31:30 +01:00
#include "world/Climate.h"
2021-03-27 10:35:02 +01:00
#include "world/EntityList.h"
#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
#include <cstdint>
2018-12-16 00:19:36 +01:00
#include <ctime>
2018-12-30 00:10:44 +01:00
#include <numeric>
#include <string_view>
#include <vector>
2018-12-15 01:37:15 +01:00
namespace OpenRCT2
{
constexpr uint32_t PARK_FILE_MAGIC = 0x4B524150; // PARK
2018-12-15 01:37:15 +01:00
// Current version that is saved.
constexpr uint32_t PARK_FILE_CURRENT_VERSION = 0x0;
2018-12-15 01:37:15 +01:00
// The minimum version that is forwards compatible with the current version.
constexpr uint32_t PARK_FILE_MIN_VERSION = 0x0;
2018-12-15 01:37:15 +01:00
namespace ParkFileChunkType
{
// clang-format off
// 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;
// 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 EDITOR = 0x21;
2018-12-16 01:24:35 +01:00
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;
// constexpr uint32_t ANIMATIONS = 0x34;
// constexpr uint32_t STAFF = 0x35;
2020-02-08 12:30:12 +01:00
constexpr uint32_t CHEATS = 0x36;
2018-12-16 01:24:35 +01:00
// constexpr uint32_t DERIVED = 0x50;
// clang-format on
}; // namespace ParkFileChunkType
2018-12-15 03:20:42 +01:00
class ParkFile
2018-12-15 03:20:42 +01:00
{
public:
2021-04-01 02:13:31 +02:00
ObjectList RequiredObjects;
2018-12-15 01:37:15 +01:00
private:
2018-12-30 03:03:24 +01:00
std::unique_ptr<OrcaStream> _os;
2018-12-30 00:10:44 +01:00
public:
void Load(const std::string_view& path)
2018-12-15 11:34:03 +01:00
{
2018-12-30 03:03:24 +01:00
_os = std::make_unique<OrcaStream>(path, OrcaStream::Mode::READING);
2021-04-01 02:13:31 +02:00
RequiredObjects = {};
2018-12-30 03:05:29 +01:00
ReadWriteObjectsChunk(*_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);
2018-12-30 00:10:44 +01:00
// Initial cash will eventually be removed
gInitialCash = gCash;
String::Set(gS6Info.name, sizeof(gS6Info.name), gScenarioName.c_str());
String::Set(gS6Info.details, sizeof(gS6Info.details), gScenarioName.c_str());
2018-12-30 03:17:33 +01:00
2019-07-16 18:56:41 +02:00
AutoDeriveVariables();
2018-12-15 03:20:42 +01:00
}
void Save(const std::string_view& path)
{
2018-12-30 03:03:24 +01:00
OrcaStream os(path, 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);
}
2018-12-15 03:20:42 +01:00
private:
2018-12-30 03:05:29 +01:00
void ReadWriteAuthoringChunk(OrcaStream& os)
{
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) {});
2018-12-30 03:03:24 +01:00
cs.Write(std::string_view()); // custom notes that can be attached to the save
cs.Write<uint64_t>(std::time(0)); // date started
cs.Write<uint64_t>(std::time(0)); // date modified
2018-12-30 00:10:44 +01:00
});
}
}
2018-12-15 15:43:24 +01:00
2018-12-30 03:05:29 +01:00
void ReadWriteObjectsChunk(OrcaStream& os)
{
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)
{
2021-04-01 02:13:31 +02:00
ObjectList requiredObjects;
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-04-01 02:13:31 +02:00
auto kind = cs.Read<uint8_t>();
ObjectEntryDescriptor desc;
switch (kind)
{
case DESCRIPTOR_NONE:
break;
case DESCRIPTOR_DAT:
desc.Entry = cs.Read<rct_object_entry>();
requiredObjects.SetObject(j, desc);
break;
case DESCRIPTOR_JSON:
desc.Type = objectType;
desc.Identifier = cs.Read<std::string>();
desc.Version = cs.Read<std::string>();
requiredObjects.SetObject(j, desc);
break;
default:
throw std::runtime_error("Unknown object descriptor kind.");
}
}
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-30 00:10:44 +01:00
else
{
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-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);
cs.Write(entry.Entry);
}
}
else
{
2021-04-01 02:13:31 +02:00
cs.Write(DESCRIPTOR_NONE);
}
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 13:46:32 +01:00
2018-12-30 03:05:29 +01:00
void ReadWriteScenarioChunk(OrcaStream& os)
{
2018-12-30 03:03:24 +01:00
os.ReadWriteChunk(ParkFileChunkType::SCENARIO, [](OrcaStream::ChunkStream& cs) {
cs.ReadWriteAs<uint8_t, uint32_t>(gS6Info.category);
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
// cs.ReadWriteAs<uint8_t, uint32_t>(gScenarioObjectiveType);
// cs.ReadWriteAs<uint8_t, uint16_t>(gScenarioObjectiveYear); // year
// cs.ReadWriteAs<uint16_t, uint32_t>(gScenarioObjectiveNumGuests); // guests
// cs.Write<uint16_t>(600); // rating
// cs.ReadWriteAs<money32, uint16_t>(gScenarioObjectiveCurrency); // excitement
// cs.ReadWriteAs<uint16_t, uint32_t>(gScenarioObjectiveNumGuests); // length
// cs.ReadWrite<money32>(gScenarioObjectiveCurrency); // park value
// cs.ReadWrite<money32>(gScenarioObjectiveCurrency); // ride profit
// cs.ReadWrite<money32>(gScenarioObjectiveCurrency); // shop profit
cs.ReadWrite<Objective>(gScenarioObjective);
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);
2018-12-30 00:10:44 +01:00
if (gScenarioCompletedCompanyValue == MONEY32_UNDEFINED
2020-06-02 22:59:26 +02:00
|| gScenarioCompletedCompanyValue == COMPANY_VALUE_ON_FAILED_OBJECTIVE)
{
2018-12-30 03:03:24 +01:00
cs.Write("");
}
else
{
2018-12-30 03:03:24 +01:00
cs.ReadWrite(gScenarioCompletedBy);
}
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-30 03:03:24 +01:00
auto found = os.ReadWriteChunk(ParkFileChunkType::GENERAL, [](OrcaStream::ChunkStream& cs) {
cs.ReadWriteAs<uint32_t, uint64_t>(gScenarioTicks);
cs.ReadWriteAs<uint16_t, uint32_t>(gDateMonthTicks);
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);
}
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);
cs.ReadWrite(gGrassSceneryTileLoopPosition); // TODO (this needs to be xy32)
2018-12-29 20:43:16 +01:00
});
if (!found)
{
2018-12-29 20:43:16 +01:00
throw std::runtime_error("No general chunk found.");
}
}
2018-12-15 13:46:32 +01:00
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) {
2020-03-04 14:43:51 +01:00
cs.ReadWrite(gSavedView);
2018-12-30 03:03:24 +01:00
cs.ReadWrite(gSavedViewZoom);
cs.ReadWrite(gSavedViewRotation);
cs.ReadWriteAs<uint8_t, uint32_t>(gLastEntranceStyle);
2018-12-30 00:10:44 +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);
});
}
2018-12-30 03:05:29 +01:00
void ReadWriteClimateChunk(OrcaStream& os)
{
2018-12-30 03:03:24 +01:00
os.ReadWriteChunk(ParkFileChunkType::CLIMATE, [](OrcaStream::ChunkStream& cs) {
cs.ReadWrite(gClimate);
cs.ReadWrite(gClimateUpdateTimer);
2018-12-30 03:03:24 +01:00
for (const auto* cl : { &gClimateCurrent, &gClimateNext })
{
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-30 00:10:44 +01:00
});
}
2018-12-15 13:46:32 +01:00
2018-12-30 03:05:29 +01:00
void ReadWriteParkChunk(OrcaStream& os)
{
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);
cs.ReadWriteAs<uint8_t, uint16_t>(gBankLoanInterestRate);
cs.ReadWriteAs<uint32_t, uint64_t>(gParkFlags);
cs.ReadWriteAs<money16, money32>(gParkEntranceFee);
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
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-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-30 03:03:24 +01:00
cs.ReadWrite(gParkRatingCasualtyPenalty);
cs.ReadWrite(gCurrentExpenditure);
cs.ReadWrite(gCurrentProfit);
cs.ReadWrite(gTotalAdmissions);
cs.ReadWrite(gTotalIncomeFromAdmissions);
2018-12-30 00:10:44 +01:00
});
}
2018-12-15 15:43:24 +01:00
2018-12-30 03:05:29 +01:00
void ReadWriteResearchChunk(OrcaStream& os)
{
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);
ReadWriteResearchItem(cs, gResearchLastItem);
ReadWriteResearchItem(cs, gResearchNextItem);
2018-12-30 00:10:44 +01: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 14:52:52 +01: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 ReadWriteDerivedChunk(OrcaStream& os)
2018-12-15 16:26:51 +01:00
{
2018-12-30 03:03:24 +01:00
if (os.GetMode() == OrcaStream::Mode::WRITING)
2018-12-15 16:26:51 +01:00
{
2018-12-30 03:03:24 +01:00
os.ReadWriteChunk(ParkFileChunkType::NOTIFICATIONS, [](OrcaStream::ChunkStream& cs) {
cs.Write<uint32_t>(gParkSize);
cs.Write<uint32_t>(gNumGuestsInPark);
cs.Write<uint32_t>(gNumGuestsHeadingForPark);
cs.Write<uint32_t>(gCompanyValue);
cs.Write<uint32_t>(gParkValue);
cs.Write<uint32_t>(gParkRating);
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 ReadWriteTilesChunk(OrcaStream& os)
{
2018-12-30 03:03:24 +01:00
auto found = os.ReadWriteChunk(ParkFileChunkType::TILES, [](OrcaStream::ChunkStream& cs) {
cs.ReadWriteAs<int16_t, uint32_t>(gMapSize); // x
cs.Write<uint32_t>(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-30 00:10:44 +01:00
OpenRCT2::GetContext()->GetGameState()->InitAll(gMapSize);
}
2018-12-30 00:10:44 +01:00
std::vector<TileElement> tiles(std::begin(gTileElements), std::end(gTileElements));
2019-01-03 16:32:45 +01:00
cs.ReadWriteVector(tiles, [&cs](TileElement& el) { cs.ReadWrite(&el, sizeof(TileElement)); });
2018-12-30 00:10:44 +01:00
std::copy_n(tiles.data(), std::min(tiles.size(), std::size(gTileElements)), gTileElements);
2018-12-15 14:52:52 +01:00
2018-12-30 00:10:44 +01:00
map_update_tile_pointers();
UpdateParkEntranceLocations();
});
if (!found)
{
2018-12-30 00:10:44 +01:00
throw std::runtime_error("No tiles chunk found.");
}
}
2018-12-15 14:52:52 +01:00
2019-08-18 23:18:22 +02:00
void ReadWriteBannersChunk(OrcaStream& os)
{
os.ReadWriteChunk(ParkFileChunkType::BANNERS, [](OrcaStream::ChunkStream& cs) {
std::vector<Banner> banners;
if (cs.GetMode() == OrcaStream::Mode::WRITING)
{
for (BannerIndex i = 0; i < MAX_BANNERS; i++)
{
banners.push_back(*GetBanner(i));
}
}
cs.ReadWriteVector(banners, [&cs](Banner& banner) {
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);
});
if (cs.GetMode() == OrcaStream::Mode::READING)
{
for (BannerIndex i = 0; i < MAX_BANNERS; i++)
{
auto banner = GetBanner(i);
*banner = banners[i];
}
}
});
}
2018-12-31 14:05:44 +01:00
void ReadWriteRidesChunk(OrcaStream& os)
{
os.ReadWriteChunk(ParkFileChunkType::RIDES, [](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
{
for (const auto& ride : GetRideManager())
2019-01-03 16:32:45 +01:00
{
2019-08-18 23:04:47 +02:00
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
2020-06-02 22:59:26 +02:00
cs.ReadWrite(ride.price[0]);
cs.ReadWrite(ride.price[1]);
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) {
cs.ReadWrite(vc);
return true;
});
2018-12-31 14:05:44 +01:00
// Stations
cs.ReadWrite(ride.num_stations);
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);
return true;
2018-12-31 14:05:44 +01:00
});
cs.ReadWrite(ride.overall_view);
// 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);
cs.ReadWrite(ride.num_customers);
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);
cs.ReadWrite(ride.downtime_history);
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.flags);
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);
2020-08-28 19:21:30 +02:00
cs.ReadWrite(entity.Name);
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);
cs.ReadWrite(entity.Intensity);
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);
cs.ReadWriteArray(entity.RidesBeenOn, [&cs](ride_id_t rideId) {
cs.ReadWrite(rideId);
return true;
});
cs.ReadWrite(entity.Id);
cs.ReadWrite(entity.CashInPocket);
cs.ReadWrite(entity.CashSpent);
// 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);
cs.ReadWriteArray(entity.Thoughts, [&cs](rct_peep_thought thought) {
2021-01-08 20:13:32 +01:00
if (thought.type != PeepThoughtType::None)
2020-08-28 19:21:30 +02:00
{
cs.ReadWrite(thought.type);
cs.ReadWrite(thought.item);
cs.ReadWrite(thought.freshness);
cs.ReadWrite(thought.fresh_timeout);
return true;
}
else
{
return false;
}
});
cs.ReadWrite(entity.PathCheckOptimisation);
cs.ReadWrite(entity.GuestHeadingToRideId);
cs.ReadWrite(entity.GuestIsLostCountdown);
cs.ReadWrite(entity.Photo1RideRef);
cs.ReadWrite(entity.PeepFlags);
cs.ReadWrite(entity.PathfindGoal);
for (size_t i = 0; i < std::size(entity.PathfindHistory); i++)
{
cs.ReadWrite(entity.PathfindHistory[i]);
}
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);
}
template<typename T> void WriteEntitiesOfType(OrcaStream::ChunkStream& cs);
template<typename... T> void WriteEntitiesOfTypes(OrcaStream::ChunkStream& cs);
2021-03-27 11:12:12 +01:00
template<typename T> void ReadEntitiesOfType(OrcaStream::ChunkStream& cs);
2021-03-27 11:12:12 +01:00
template<typename... T> void ReadEntitiesOfTypes(OrcaStream::ChunkStream& cs);
2021-03-27 11:12:12 +01:00
void ReadWriteEntitiesChunk(OrcaStream& os);
2020-08-28 19:21:30 +02:00
2019-07-16 18:56:41 +02:00
void AutoDeriveVariables()
{
uint16_t numGuestsInPark = 0;
uint16_t numGuestsHeadingsForPark = 0;
2021-03-27 10:35:02 +01:00
for (auto guest : EntityList<Guest>())
2019-07-16 18:56:41 +02:00
{
2021-03-27 10:35:02 +01:00
if (guest->State == PeepState::EnteringPark)
2019-07-16 18:56:41 +02:00
{
numGuestsHeadingsForPark++;
}
2021-03-27 10:35:02 +01:00
if (!guest->OutsideOfPark)
2019-07-16 18:56:41 +02:00
{
numGuestsInPark++;
}
}
gNumGuestsInPark = numGuestsInPark;
gNumGuestsHeadingForPark = numGuestsHeadingsForPark;
auto& park = GetContext()->GetGameState()->GetPark();
gParkSize = park.CalculateParkSize();
gParkValue = park.CalculateParkValue();
gCompanyValue = park.CalculateCompanyValue();
gParkRating = park.CalculateParkRating();
}
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
}
}
}
};
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Vehicle& entity)
{
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);
cs.ReadWrite(entity.colours);
cs.ReadWrite(entity.track_progress);
2021-03-30 22:26:02 +02:00
cs.ReadWrite(entity.BoatLocation);
cs.ReadWrite(entity.TrackTypeAndDirection);
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);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Guest& entity)
{
ReadWritePeep(cs, entity);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Staff& entity)
{
ReadWritePeep(cs, entity);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, SteamParticle& steamParticle)
{
ReadWriteEntityCommon(cs, steamParticle);
cs.ReadWrite(steamParticle.time_to_move);
cs.ReadWrite(steamParticle.frame);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, MoneyEffect& moneyEffect)
{
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);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, VehicleCrashParticle& vehicleCrashParticle)
{
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);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, ExplosionCloud& entity)
{
ReadWriteEntityCommon(cs, entity);
cs.ReadWrite(entity.frame);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, CrashSplashParticle& entity)
{
ReadWriteEntityCommon(cs, entity);
cs.ReadWrite(entity.frame);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, ExplosionFlare& entity)
{
ReadWriteEntityCommon(cs, entity);
cs.ReadWrite(entity.frame);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, JumpingFountain& fountain)
{
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);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Balloon& balloon)
{
ReadWriteEntityCommon(cs, balloon);
cs.ReadWrite(balloon.popped);
cs.ReadWrite(balloon.time_to_move);
cs.ReadWrite(balloon.frame);
cs.ReadWrite(balloon.colour);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Duck& duck)
{
ReadWriteEntityCommon(cs, duck);
cs.ReadWrite(duck.frame);
cs.ReadWrite(duck.target_x);
cs.ReadWrite(duck.target_y);
cs.ReadWrite(duck.state);
}
template<> void ParkFile::ReadWriteEntity(OrcaStream::ChunkStream& cs, Litter& entity)
{
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>();
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{};
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;
}
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);
}
});
}
} // 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)
{
map_reorganise_elements();
auto parkFile = std::make_unique<OpenRCT2::ParkFile>();
parkFile->Save(path);
}
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();
}
map_reorganise_elements();
viewport_set_saved_view();
bool result = false;
auto parkFile = std::make_unique<OpenRCT2::ParkFile>();
2018-12-15 03:20:42 +01:00
try
{
// if (flags & S6_SAVE_FLAG_EXPORT)
// {
// auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
// s6exporter->ExportObjectsList = objManager.GetPackableObjects();
// }
// s6exporter->RemoveTracklessRides = true;
// s6exporter->Export();
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
class ParkFileImporter : public IParkImporter
{
private:
2019-10-27 18:19:35 +01:00
#ifdef __clang__
[[maybe_unused]]
#endif
const IObjectRepository& _objectRepository;
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
{
_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
{
return Load(path);
}
void Import() override
{
2018-12-15 14:52:52 +01:00
_parkFile->Import();
2018-12-15 13:46:32 +01:00
}
bool GetDetails(scenario_index_entry* dst) override
{
return false;
}
};
std::unique_ptr<IParkImporter> ParkImporter::CreateParkFile(IObjectRepository& objectRepository)
{
return std::make_unique<ParkFileImporter>(objectRepository);
}