diff --git a/src/openrct2/PlatformEnvironment.cpp b/src/openrct2/PlatformEnvironment.cpp
index d47f1daa16..db9f90b2f6 100644
--- a/src/openrct2/PlatformEnvironment.cpp
+++ b/src/openrct2/PlatformEnvironment.cpp
@@ -39,26 +39,27 @@ static constexpr const char* DirectoryNamesRCT2[] = {
};
static constexpr u8string_view DirectoryNamesOpenRCT2[] = {
- u8"data", // DATA
- u8"landscape", // LANDSCAPE
- u8"language", // LANGUAGE
- u8"chatlogs", // LOG_CHAT
- u8"serverlogs", // LOG_SERVER
- u8"keys", // NETWORK_KEY
- u8"object", // OBJECT
- u8"plugin", // PLUGIN
- u8"save", // SAVE
- u8"scenario", // SCENARIO
- u8"screenshot", // SCREENSHOT
- u8"sequence", // SEQUENCE
- u8"shaders", // SHADER
- u8"themes", // THEME
- u8"track", // TRACK
- u8"heightmap", // HEIGHTMAP
- u8"replay", // REPLAY
- u8"desyncs", // DESYNCS
- u8"crash", // CRASH
- u8"assetpack", // ASSET_PACK
+ u8"data", // DATA
+ u8"landscape", // LANDSCAPE
+ u8"language", // LANGUAGE
+ u8"chatlogs", // LOG_CHAT
+ u8"serverlogs", // LOG_SERVER
+ u8"keys", // NETWORK_KEY
+ u8"object", // OBJECT
+ u8"plugin", // PLUGIN
+ u8"save", // SAVE
+ u8"scenario", // SCENARIO
+ u8"screenshot", // SCREENSHOT
+ u8"sequence", // SEQUENCE
+ u8"shaders", // SHADER
+ u8"themes", // THEME
+ u8"track", // TRACK
+ u8"heightmap", // HEIGHTMAP
+ u8"replay", // REPLAY
+ u8"desyncs", // DESYNCS
+ u8"crash", // CRASH
+ u8"assetpack", // ASSET_PACK
+ u8"scenario_patches", // SCENARIO_PATCHES
};
static constexpr u8string_view FileNames[] = {
diff --git a/src/openrct2/PlatformEnvironment.h b/src/openrct2/PlatformEnvironment.h
index b716da22df..e9f6d5c118 100644
--- a/src/openrct2/PlatformEnvironment.h
+++ b/src/openrct2/PlatformEnvironment.h
@@ -32,26 +32,27 @@ namespace OpenRCT2
enum class DIRID
{
- DATA, // Contains g1.dat, music etc.
- LANDSCAPE, // Contains scenario editor landscapes (SC6).
- LANGUAGE, // Contains language packs.
- LOG_CHAT, // Contains chat logs.
- LOG_SERVER, // Contains server logs.
- NETWORK_KEY, // Contains the user's public and private keys.
- OBJECT, // Contains objects.
- PLUGIN, // Contains plugins (.js).
- SAVE, // Contains saved games (SV6).
- SCENARIO, // Contains scenarios (SC6).
- SCREENSHOT, // Contains screenshots.
- SEQUENCE, // Contains title sequences.
- SHADER, // Contains OpenGL shaders.
- THEME, // Contains interface themes.
- TRACK, // Contains track designs.
- HEIGHTMAP, // Contains heightmap data.
- REPLAY, // Contains recorded replays.
- LOG_DESYNCS, // Contains desync reports.
- CRASH, // Contains crash dumps.
- ASSET_PACK, // Contains asset packs.
+ DATA, // Contains g1.dat, music etc.
+ LANDSCAPE, // Contains scenario editor landscapes (SC6).
+ LANGUAGE, // Contains language packs.
+ LOG_CHAT, // Contains chat logs.
+ LOG_SERVER, // Contains server logs.
+ NETWORK_KEY, // Contains the user's public and private keys.
+ OBJECT, // Contains objects.
+ PLUGIN, // Contains plugins (.js).
+ SAVE, // Contains saved games (SV6).
+ SCENARIO, // Contains scenarios (SC6).
+ SCREENSHOT, // Contains screenshots.
+ SEQUENCE, // Contains title sequences.
+ SHADER, // Contains OpenGL shaders.
+ THEME, // Contains interface themes.
+ TRACK, // Contains track designs.
+ HEIGHTMAP, // Contains heightmap data.
+ REPLAY, // Contains recorded replays.
+ LOG_DESYNCS, // Contains desync reports.
+ CRASH, // Contains crash dumps.
+ ASSET_PACK, // Contains asset packs.
+ SCENARIO_PATCHES, // Contains scenario patches.
};
enum class PATHID
diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj
index f70648b2d0..ee73abdf55 100644
--- a/src/openrct2/libopenrct2.vcxproj
+++ b/src/openrct2/libopenrct2.vcxproj
@@ -995,6 +995,7 @@
+
diff --git a/src/openrct2/rct12/ScenarioPatcher.cpp b/src/openrct2/rct12/ScenarioPatcher.cpp
new file mode 100644
index 0000000000..17814a7ace
--- /dev/null
+++ b/src/openrct2/rct12/ScenarioPatcher.cpp
@@ -0,0 +1,129 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2024 OpenRCT2 developers
+ *
+ * For a complete list of all authors, please refer to contributors.md
+ * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
+ *
+ * OpenRCT2 is licensed under the GNU General Public License version 3.
+ *****************************************************************************/
+
+#include "ScenarioPatcher.h"
+
+#include "../Context.h"
+#include "../PlatformEnvironment.h"
+#include "../core/File.h"
+#include "../core/Guard.hpp"
+#include "../core/Json.hpp"
+#include "../core/Path.hpp"
+#include "../world/Location.hpp"
+#include "../world/Map.h"
+#include "../world/Surface.h"
+
+#include
+
+static u8string ToOwnershipJsonKey(int ownershipType)
+{
+ switch (ownershipType)
+ {
+ case OWNERSHIP_UNOWNED:
+ return "unowned";
+ case OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED:
+ return "construction_rights_owned";
+ case OWNERSHIP_OWNED:
+ return "owned";
+ case OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE:
+ return "construction_rights_available";
+ case OWNERSHIP_AVAILABLE:
+ return "available";
+ }
+ Guard::Assert(0, "Unrecognized ownership type flag");
+ return {};
+}
+
+static void ApplyLandOwnershipFixes(const json_t& landOwnershipFixes, int ownershipType)
+{
+ auto ownershipTypeKey = ToOwnershipJsonKey(ownershipType);
+ if (!landOwnershipFixes.contains(ownershipTypeKey))
+ {
+ return;
+ }
+
+ auto ownershipParameters = landOwnershipFixes[ownershipTypeKey];
+ constexpr u8string_view coordinatesKey = "coordinates";
+ if (!ownershipParameters.contains(coordinatesKey))
+ {
+ Guard::Assert(0, "Cannot have ownership fix without coordinates array");
+ return;
+ }
+ else if (!ownershipParameters[coordinatesKey].is_array())
+ {
+ Guard::Assert(0, "Ownership fix coordinates should be an array");
+ return;
+ }
+
+ auto ownershipCoords = Json::AsArray(ownershipParameters[coordinatesKey]);
+ if (ownershipCoords.empty())
+ {
+ Guard::Assert(0, "Ownership fix coordinates array should not be empty");
+ return;
+ }
+
+ const bool cannotDowngrade = ownershipParameters.contains("cannot_downgrade")
+ ? Json::GetBoolean(ownershipParameters["cannot_downgrade"], false)
+ : false;
+ std::initializer_list tiles;
+ for (size_t i = 0; i < ownershipCoords.size(); ++i)
+ {
+ if (!ownershipCoords[i].is_array())
+ {
+ Guard::Assert(0, "Ownership fix coordinates should contain only arrays");
+ return;
+ }
+
+ auto coordinatesPair = Json::AsArray(ownershipCoords[i]);
+ if (coordinatesPair.size() != 2)
+ {
+ Guard::Assert(0, "Ownership fix coordinates sub array should have 2 elements");
+ return;
+ }
+ FixLandOwnershipTilesWithOwnership(
+ { { Json::GetNumber(coordinatesPair[0]), Json::GetNumber(coordinatesPair[1]) } }, ownershipType,
+ cannotDowngrade);
+ }
+}
+
+static void ApplyLandOwnershipFixes(const json_t& scenarioPatch)
+{
+ constexpr u8string_view landOwnershipKey = "land_ownership";
+ if (!scenarioPatch.contains(landOwnershipKey))
+ {
+ return;
+ }
+
+ auto landOwnershipFixes = scenarioPatch[landOwnershipKey];
+ for (const auto& ownershipType : { OWNERSHIP_UNOWNED, OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED, OWNERSHIP_OWNED,
+ OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE, OWNERSHIP_AVAILABLE })
+ {
+ ApplyLandOwnershipFixes(landOwnershipFixes, ownershipType);
+ }
+}
+
+static u8string GetPatchFileName(u8string_view scenarioName)
+{
+ auto env = OpenRCT2::GetContext()->GetPlatformEnvironment();
+ auto scenarioPatches = env->GetDirectoryPath(OpenRCT2::DIRBASE::OPENRCT2, OpenRCT2::DIRID::SCENARIO_PATCHES);
+ auto scenarioPatchFile = Path::WithExtension(Path::GetFileNameWithoutExtension(scenarioName), ".json");
+ return Path::Combine(scenarioPatches, scenarioPatchFile);
+}
+
+void RCT12::FetchAndApplyScenarioPatch(u8string_view scenarioName)
+{
+ auto patchPath = GetPatchFileName(scenarioName);
+ std::cout << "Path is: " << patchPath << std::endl;
+ // TODO: Check if case sensitive, some scenario names have all lowercase variations
+ if (File::Exists(patchPath))
+ {
+ auto scenarioPatch = Json::ReadFromFile(patchPath);
+ ApplyLandOwnershipFixes(scenarioPatch);
+ }
+}
\ No newline at end of file
diff --git a/src/openrct2/rct12/ScenarioPatcher.h b/src/openrct2/rct12/ScenarioPatcher.h
new file mode 100644
index 0000000000..28cb9ac1ad
--- /dev/null
+++ b/src/openrct2/rct12/ScenarioPatcher.h
@@ -0,0 +1,17 @@
+/*****************************************************************************
+ * Copyright (c) 2014-2024 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 "../core/String.hpp"
+
+namespace RCT12
+{
+ void FetchAndApplyScenarioPatch(u8string_view scenarioName);
+}