diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index f01b1a0b88..3eb744c77c 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -311,6 +311,7 @@
+
diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp
index 97fa00b34d..853a8ae796 100644
--- a/src/openrct2/rct1/S4Importer.cpp
+++ b/src/openrct2/rct1/S4Importer.cpp
@@ -45,6 +45,7 @@
#include "../object/ObjectManager.h"
#include "../object/ObjectRepository.h"
#include "../peep/RideUseSystem.h"
+#include "../rct12/EntryList.h"
#include "../ride/RideData.h"
#include "../ride/Station.h"
#include "../ride/Track.h"
@@ -80,44 +81,6 @@ using namespace OpenRCT2;
namespace RCT1
{
- class EntryList
- {
- private:
- std::vector _entries;
-
- public:
- size_t GetCount() const
- {
- return _entries.size();
- }
-
- const std::vector& GetEntries() const
- {
- return _entries;
- }
-
- ObjectEntryIndex GetOrAddEntry(std::string_view identifier)
- {
- for (size_t i = 0; i < _entries.size(); i++)
- {
- if (_entries[i] == identifier)
- {
- return static_cast(i);
- }
- }
- _entries.emplace_back(identifier);
- return static_cast(_entries.size() - 1);
- }
-
- void AddRange(std::initializer_list initializerList)
- {
- for (auto entry : initializerList)
- {
- GetOrAddEntry(entry);
- }
- }
- };
-
class S4Importer final : public IParkImporter
{
private:
@@ -128,18 +91,18 @@ namespace RCT1
bool _isScenario = false;
// Lists of dynamic object entries
- EntryList _rideEntries;
- EntryList _smallSceneryEntries;
- EntryList _largeSceneryEntries;
- EntryList _wallEntries;
- EntryList _pathEntries;
- EntryList _pathAdditionEntries;
- EntryList _sceneryGroupEntries;
- EntryList _waterEntry;
- EntryList _terrainSurfaceEntries;
- EntryList _terrainEdgeEntries;
- EntryList _footpathSurfaceEntries;
- EntryList _footpathRailingsEntries;
+ RCT12::EntryList _rideEntries;
+ RCT12::EntryList _smallSceneryEntries;
+ RCT12::EntryList _largeSceneryEntries;
+ RCT12::EntryList _wallEntries;
+ RCT12::EntryList _pathEntries;
+ RCT12::EntryList _pathAdditionEntries;
+ RCT12::EntryList _sceneryGroupEntries;
+ RCT12::EntryList _waterEntry;
+ RCT12::EntryList _terrainSurfaceEntries;
+ RCT12::EntryList _terrainEdgeEntries;
+ RCT12::EntryList _footpathSurfaceEntries;
+ RCT12::EntryList _footpathRailingsEntries;
// Lookup tables for converting from RCT1 hard coded types to the new dynamic object entries
ObjectEntryIndex _rideTypeToRideEntryMap[EnumValue(RideType::Count)]{};
@@ -598,7 +561,7 @@ namespace RCT1
case ObjectType::Paths:
case ObjectType::PathBits:
{
- EntryList* entries = GetEntryList(objectType);
+ RCT12::EntryList* entries = GetEntryList(objectType);
// Check if there are spare entries available
size_t maxEntries = static_cast(object_entry_group_counts[EnumValue(objectType)]);
@@ -1484,7 +1447,7 @@ namespace RCT1
}
}
- void AppendRequiredObjects(ObjectList& objectList, ObjectType objectType, const EntryList& entryList)
+ void AppendRequiredObjects(ObjectList& objectList, ObjectType objectType, const RCT12::EntryList& entryList)
{
AppendRequiredObjects(objectList, objectType, entryList.GetEntries());
}
@@ -2456,7 +2419,7 @@ namespace RCT1
}
}
- EntryList* GetEntryList(ObjectType objectType)
+ RCT12::EntryList* GetEntryList(ObjectType objectType)
{
switch (objectType)
{
diff --git a/src/openrct2/rct12/EntryList.h b/src/openrct2/rct12/EntryList.h
new file mode 100644
index 0000000000..0b6b05de04
--- /dev/null
+++ b/src/openrct2/rct12/EntryList.h
@@ -0,0 +1,65 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2021 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.
+ *****************************************************************************/
+
+#pragma once
+
+#include
+#include
+#include
+
+using ObjectEntryIndex = uint16_t;
+
+namespace RCT12
+{
+ class EntryList
+ {
+ private:
+ std::vector _entries;
+
+ public:
+ size_t GetCount() const
+ {
+ return _entries.size();
+ }
+
+ const std::vector& GetEntries() const
+ {
+ return _entries;
+ }
+
+ ObjectEntryIndex GetOrAddEntry(std::string_view identifier)
+ {
+ for (size_t i = 0; i < _entries.size(); i++)
+ {
+ if (_entries[i] == identifier)
+ {
+ return static_cast(i);
+ }
+ }
+ _entries.emplace_back(identifier);
+ return static_cast(_entries.size() - 1);
+ }
+
+ void AddRange(std::initializer_list initializerList)
+ {
+ for (auto entry : initializerList)
+ {
+ GetOrAddEntry(entry);
+ }
+ }
+
+ template void AddRange(const std::string_view (&list)[i])
+ {
+ for (auto entry : list)
+ {
+ GetOrAddEntry(entry);
+ }
+ }
+ };
+} // namespace RCT12
diff --git a/src/openrct2/rct12/RCT12.cpp b/src/openrct2/rct12/RCT12.cpp
index b7ce369b01..6eeddf34de 100644
--- a/src/openrct2/rct12/RCT12.cpp
+++ b/src/openrct2/rct12/RCT12.cpp
@@ -22,6 +22,7 @@
#include "../world/Surface.h"
#include "../world/TileElement.h"
#include "../world/Wall.h"
+#include "EntryList.h"
using namespace OpenRCT2;
@@ -889,46 +890,6 @@ std::optional GetStyleFromMusicIdentifier(std::string_view identifier)
return std::nullopt;
}
-void SetDefaultRCT2TerrainObjects(ObjectList& objectList)
-{
- // Surfaces
- objectList.SetObject(ObjectType::TerrainSurface, 0, "rct2.terrain_surface.grass");
- objectList.SetObject(ObjectType::TerrainSurface, 1, "rct2.terrain_surface.sand");
- objectList.SetObject(ObjectType::TerrainSurface, 2, "rct2.terrain_surface.dirt");
- objectList.SetObject(ObjectType::TerrainSurface, 3, "rct2.terrain_surface.rock");
- objectList.SetObject(ObjectType::TerrainSurface, 4, "rct2.terrain_surface.martian");
- objectList.SetObject(ObjectType::TerrainSurface, 5, "rct2.terrain_surface.chequerboard");
- objectList.SetObject(ObjectType::TerrainSurface, 6, "rct2.terrain_surface.grass_clumps");
- objectList.SetObject(ObjectType::TerrainSurface, 7, "rct2.terrain_surface.ice");
- objectList.SetObject(ObjectType::TerrainSurface, 8, "rct2.terrain_surface.grid_red");
- objectList.SetObject(ObjectType::TerrainSurface, 9, "rct2.terrain_surface.grid_yellow");
- objectList.SetObject(ObjectType::TerrainSurface, 10, "rct2.terrain_surface.grid_purple");
- objectList.SetObject(ObjectType::TerrainSurface, 11, "rct2.terrain_surface.grid_green");
- objectList.SetObject(ObjectType::TerrainSurface, 12, "rct2.terrain_surface.sand_red");
- objectList.SetObject(ObjectType::TerrainSurface, 13, "rct2.terrain_surface.sand_brown");
- objectList.SetObject(ObjectType::TerrainSurface, 14, "rct1aa.terrain_surface.roof_red");
- objectList.SetObject(ObjectType::TerrainSurface, 15, "rct1ll.terrain_surface.roof_grey");
- objectList.SetObject(ObjectType::TerrainSurface, 16, "rct1ll.terrain_surface.rust");
- objectList.SetObject(ObjectType::TerrainSurface, 17, "rct1ll.terrain_surface.wood");
-
- // Edges
- objectList.SetObject(ObjectType::TerrainEdge, 0, "rct2.terrain_edge.rock");
- objectList.SetObject(ObjectType::TerrainEdge, 1, "rct2.terrain_edge.wood_red");
- objectList.SetObject(ObjectType::TerrainEdge, 2, "rct2.terrain_edge.wood_black");
- objectList.SetObject(ObjectType::TerrainEdge, 3, "rct2.terrain_edge.ice");
- objectList.SetObject(ObjectType::TerrainEdge, 4, "rct1.terrain_edge.brick");
- objectList.SetObject(ObjectType::TerrainEdge, 5, "rct1.terrain_edge.iron");
- objectList.SetObject(ObjectType::TerrainEdge, 6, "rct1aa.terrain_edge.grey");
- objectList.SetObject(ObjectType::TerrainEdge, 7, "rct1aa.terrain_edge.yellow");
- objectList.SetObject(ObjectType::TerrainEdge, 8, "rct1aa.terrain_edge.red");
- objectList.SetObject(ObjectType::TerrainEdge, 9, "rct1ll.terrain_edge.purple");
- objectList.SetObject(ObjectType::TerrainEdge, 10, "rct1ll.terrain_edge.green");
- objectList.SetObject(ObjectType::TerrainEdge, 11, "rct1ll.terrain_edge.stone_brown");
- objectList.SetObject(ObjectType::TerrainEdge, 12, "rct1ll.terrain_edge.stone_grey");
- objectList.SetObject(ObjectType::TerrainEdge, 13, "rct1ll.terrain_edge.skyscraper_a");
- objectList.SetObject(ObjectType::TerrainEdge, 14, "rct1ll.terrain_edge.skyscraper_b");
-}
-
void RCT12AddDefaultObjects(ObjectList& objectList)
{
// Stations
@@ -947,6 +908,21 @@ void RCT12AddDefaultObjects(ObjectList& objectList)
}
}
+static void AppendRequiredObjects(ObjectList& objectList, ObjectType objectType, const std::vector& objectNames)
+{
+ for (const auto& objectName : objectNames)
+ {
+ auto descriptor = ObjectEntryDescriptor(objectName);
+ descriptor.Type = objectType;
+ objectList.Add(descriptor);
+ }
+}
+
+void AppendRequiredObjects(ObjectList& objectList, ObjectType objectType, const RCT12::EntryList& entryList)
+{
+ AppendRequiredObjects(objectList, objectType, entryList.GetEntries());
+}
+
money64 RCT12CompletedCompanyValueToOpenRCT2(money32 origValue)
{
if (origValue == RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE)
diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h
index 9616db715b..f53a51b743 100644
--- a/src/openrct2/rct12/RCT12.h
+++ b/src/openrct2/rct12/RCT12.h
@@ -24,6 +24,10 @@ class ObjectList;
using track_type_t = uint16_t;
using RCT12TrackType = uint8_t;
+namespace RCT12
+{
+ class EntryList;
+}
constexpr uint8_t RCT2_STRING_FORMAT_ARG_START = 123;
constexpr uint8_t RCT2_STRING_FORMAT_ARG_END = 141;
@@ -832,8 +836,8 @@ track_type_t RCT12FlatTrackTypeToOpenRCT2(RCT12TrackType origTrackType);
RCT12TrackType OpenRCT2FlatTrackTypeToRCT12(track_type_t origTrackType);
std::string_view GetStationIdentifierFromStyle(uint8_t style);
std::optional GetStyleFromMusicIdentifier(std::string_view identifier);
-void SetDefaultRCT2TerrainObjects(ObjectList& objectList);
void RCT12AddDefaultObjects(ObjectList& objectList);
+void AppendRequiredObjects(ObjectList& objectList, ObjectType objectType, const RCT12::EntryList& entryList);
static constexpr money32 RCT12_COMPANY_VALUE_ON_FAILED_OBJECTIVE = 0x80000001;
diff --git a/src/openrct2/rct2/RCT2.cpp b/src/openrct2/rct2/RCT2.cpp
index 89eb75e1f3..6f72425695 100644
--- a/src/openrct2/rct2/RCT2.cpp
+++ b/src/openrct2/rct2/RCT2.cpp
@@ -11,6 +11,7 @@
#include "../Context.h"
#include "../object/Object.h"
+#include "../object/ObjectList.h"
#include "../object/ObjectManager.h"
#include "../ride/Ride.h"
#include "../ride/RideData.h"
diff --git a/src/openrct2/rct2/RCT2.h b/src/openrct2/rct2/RCT2.h
index 1826a97a27..5fdc6cc977 100644
--- a/src/openrct2/rct2/RCT2.h
+++ b/src/openrct2/rct2/RCT2.h
@@ -20,6 +20,7 @@
#include
struct rct_ride_entry;
+class ObjectList;
enum class EditorStep : uint8_t;
namespace RCT2
@@ -1063,6 +1064,37 @@ namespace RCT2
const FootpathMapping* GetFootpathSurfaceId(
const ObjectEntryDescriptor& desc, bool ideallyLoaded = false, bool isQueue = false);
std::optional GetBestObjectEntryForSurface(std::string_view surface, std::string_view railings);
+
+ static constexpr std::string_view DefaultTerrainSurfaces[] = {
+ "rct2.terrain_surface.grass", "rct2.terrain_surface.sand", "rct2.terrain_surface.dirt",
+ "rct2.terrain_surface.rock", "rct2.terrain_surface.martian", "rct2.terrain_surface.chequerboard",
+ "rct2.terrain_surface.grass_clumps", "rct2.terrain_surface.ice", "rct2.terrain_surface.grid_red",
+ "rct2.terrain_surface.grid_yellow", "rct2.terrain_surface.grid_purple", "rct2.terrain_surface.grid_green",
+ "rct2.terrain_surface.sand_red", "rct2.terrain_surface.sand_brown",
+ };
+
+ // Additional surface styles added to OpenRCT2 as a feature if RCT1 linked
+ static constexpr std::string_view OpenRCT2HybridTerrainSurfaces[] = {
+ "rct1aa.terrain_surface.roof_red",
+ "rct1ll.terrain_surface.roof_grey",
+ "rct1ll.terrain_surface.rust",
+ "rct1ll.terrain_surface.wood",
+ };
+
+ static constexpr std::string_view DefaultTerrainEdges[] = {
+ "rct2.terrain_edge.rock",
+ "rct2.terrain_edge.wood_red",
+ "rct2.terrain_edge.wood_black",
+ "rct2.terrain_edge.ice",
+ };
+
+ // Additional surface edges added to OpenRCT2 as a feature if RCT1 was linked
+ static constexpr std::string_view OpenRCT2HybridTerrainEdges[] = {
+ "rct1.terrain_edge.brick", "rct1.terrain_edge.iron", "rct1aa.terrain_edge.grey",
+ "rct1aa.terrain_edge.yellow", "rct1aa.terrain_edge.red", "rct1ll.terrain_edge.purple",
+ "rct1ll.terrain_edge.green", "rct1ll.terrain_edge.stone_brown", "rct1ll.terrain_edge.stone_grey",
+ "rct1ll.terrain_edge.skyscraper_a", "rct1ll.terrain_edge.skyscraper_b",
+ };
} // namespace RCT2
std::vector DecryptSea(const fs::path& path);
diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp
index ec27911ff6..91e09c9ad5 100644
--- a/src/openrct2/rct2/S6Importer.cpp
+++ b/src/openrct2/rct2/S6Importer.cpp
@@ -47,6 +47,7 @@
#include "../object/ObjectManager.h"
#include "../object/ObjectRepository.h"
#include "../peep/RideUseSystem.h"
+#include "../rct12/EntryList.h"
#include "../rct12/RCT12.h"
#include "../rct12/SawyerChunkReader.h"
#include "../rct12/SawyerEncoding.h"
@@ -94,6 +95,8 @@ namespace RCT2
ObjectEntryIndex _pathToSurfaceMap[16];
ObjectEntryIndex _pathToQueueSurfaceMap[16];
ObjectEntryIndex _pathToRailingMap[16];
+ RCT12::EntryList _terrainSurfaceEntries;
+ RCT12::EntryList _terrainEdgeEntries;
public:
S6Importer(IObjectRepository& objectRepository)
@@ -492,6 +495,15 @@ namespace RCT2
ClearRestrictedScenery();
}
+ void AddDefaultEntries()
+ {
+ // Add default surfaces
+ _terrainSurfaceEntries.AddRange(DefaultTerrainSurfaces);
+
+ // Add default edges
+ _terrainEdgeEntries.AddRange(DefaultTerrainEdges);
+ }
+
void ConvertScenarioStringsToUTF8()
{
// Scenario details
@@ -1192,8 +1204,10 @@ namespace RCT2
auto src2 = src->AsSurface();
dst2->SetSlope(src2->GetSlope());
+
dst2->SetSurfaceStyle(src2->GetSurfaceStyle());
dst2->SetEdgeStyle(src2->GetEdgeStyle());
+
dst2->SetGrassLength(src2->GetGrassLength());
dst2->SetOwnership(src2->GetOwnership());
dst2->SetParkFences(src2->GetParkFences());
@@ -1732,7 +1746,37 @@ namespace RCT2
}
}
- SetDefaultRCT2TerrainObjects(objectList);
+ // Add default rct2 terrain surfaces and edges
+ AddDefaultEntries();
+
+ // Find if any rct1 terrain surfaces or edges have been used
+ const bool hasRCT1Terrain = std::any_of(
+ std::begin(_s6.tile_elements), std::end(_s6.tile_elements), [](RCT12TileElement& tile) {
+ auto* surface = tile.AsSurface();
+ if (surface == nullptr)
+ {
+ return false;
+ }
+ if (surface->GetSurfaceStyle() >= std::size(RCT2::DefaultTerrainSurfaces))
+ {
+ return true;
+ }
+ if (surface->GetEdgeStyle() >= std::size(RCT2::DefaultTerrainEdges))
+ {
+ return true;
+ }
+ return false;
+ });
+
+ // If an rct1 surface or edge then load all the Hybrid surfaces and edges
+ if (hasRCT1Terrain)
+ {
+ _terrainSurfaceEntries.AddRange(OpenRCT2HybridTerrainSurfaces);
+ _terrainEdgeEntries.AddRange(OpenRCT2HybridTerrainEdges);
+ }
+
+ AppendRequiredObjects(objectList, ObjectType::TerrainSurface, _terrainSurfaceEntries);
+ AppendRequiredObjects(objectList, ObjectType::TerrainEdge, _terrainEdgeEntries);
RCT12AddDefaultObjects(objectList);
return objectList;
}