diff --git a/data/scenario_patches/0b8cc95.parkpatch b/data/scenario_patches/0b8cc95.parkpatch new file mode 100644 index 0000000000..bfbf52c33e --- /dev/null +++ b/data/scenario_patches/0b8cc95.parkpatch @@ -0,0 +1,31 @@ +{ + "scenario_name": "Rollercoaster Heaven", + "sha256": "0b8cc952c399e1515546a4ababe5ba2f7aace83915a7e51c56ee901568c9ef56", + "elements_to_delete": [ + { + "element_index": 0, + "coordinates": [ + [ 23, 79 ], [ 24, 79 ] + ] + }, + { + "element_index": 1, + "coordinates": [ + [ 87, 13 ] + ] + } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct2.footpath_surface.crazy_paving", + "coordinates": [ [ 24, 79, 14 ], [ 87, 13, 18 ] ] + }, + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct2.footpath_surface.crazy_paving", + "coordinates": [ [ 23, 79, 12 ] ], + "slope_direction": 2 + } + ] +} diff --git a/data/scenario_patches/2696a05.parkpatch b/data/scenario_patches/2696a05.parkpatch index 391ddbd0f9..54c597985b 100644 --- a/data/scenario_patches/2696a05.parkpatch +++ b/data/scenario_patches/2696a05.parkpatch @@ -18,5 +18,20 @@ [ 140, 74 ], [ 141, 74 ], [ 142, 74 ], [ 143, 74 ], [ 144, 74 ], [ 145, 74 ], [ 146, 74 ], [ 147, 74 ] ] } - } + }, + "elements_to_delete": [ + { + "element_index": 1, + "coordinates": [ + [ 86, 75 ], [ 86, 77 ], [ 86, 78 ] + ] + } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.bamboo_brown", + "surface": "rct2.footpath_surface.dirt", + "coordinates": [ [ 86, 74, 12 ], [ 86, 75, 12 ], [ 86, 76, 12 ], [ 86, 77, 12 ], [ 86, 78, 12 ] ] + } + ] } diff --git a/data/scenario_patches/2980c28.parkpatch b/data/scenario_patches/2980c28.parkpatch new file mode 100644 index 0000000000..e16ab06d8f --- /dev/null +++ b/data/scenario_patches/2980c28.parkpatch @@ -0,0 +1,21 @@ +{ + "scenario_name": "Mines of Africa", + "sha256": "2980c287d81e4e96a46db30d1a80e6bfa0caace6a14488dddc6f20ac71676cff", + "elements_to_delete": [ + { + "element_index": 1, + "coordinates": [ + [ 43, 46 ] + ] + } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct2.footpath_surface.queue_yellow", + "coordinates": [ [ 43, 46, 6 ] ], + "slope_direction": 3, + "queue": true + } + ] +} diff --git a/data/scenario_patches/4a762ae.parkpatch b/data/scenario_patches/4a762ae.parkpatch new file mode 100644 index 0000000000..01986fc1c2 --- /dev/null +++ b/data/scenario_patches/4a762ae.parkpatch @@ -0,0 +1,40 @@ +{ + "scenario_name": "Iceberg Islands", + "sha256": "4a762ae89bf279dbcaead9e457c21014a9e39eb4a46cf1cbf63304450f9ed050", + "elements_to_delete": [ + { + "element_index": 2, + "coordinates": [ + [ 49, 43 ], [ 50, 43 ], [ 51, 43 ], [ 44, 55 ] + ] + }, + { + "element_index": 1, + "coordinates": [ + [ 44, 54 ], [ 44, 53 ] + ] + } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct1.footpath_surface.queue_blue", + "coordinates": [ [ 51, 43, 24 ], [ 44, 55, 20 ] ], + "queue": true + }, + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct1.footpath_surface.queue_blue", + "coordinates": [ [ 50, 43, 24 ], [ 49, 43, 26 ] ], + "slope_direction": 0, + "queue": true + }, + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct1.footpath_surface.queue_blue", + "coordinates": [ [ 44, 54, 18 ], [ 44, 53, 16 ] ], + "slope_direction": 1, + "queue": true + } + ] +} diff --git a/data/scenario_patches/5c95a4e.parkpatch b/data/scenario_patches/5c95a4e.parkpatch index 6c30ef46bf..67b6961874 100644 --- a/data/scenario_patches/5c95a4e.parkpatch +++ b/data/scenario_patches/5c95a4e.parkpatch @@ -7,5 +7,18 @@ [ 11, 31 ], [ 68, 112 ], [ 72, 118 ] ] } - } + }, + "paths": [ + { + "railings": "rct2.footpath_railings.wood", + "surface": "rct1.footpath_surface.crazy_paving", + "coordinates": [ [ 83, 81, 22 ] ] + }, + { + "railings": "rct2.footpath_railings.wood", + "surface": "rct1.footpath_surface.crazy_paving", + "coordinates": [ [ 85, 77, 22 ] ], + "slope_direction": 2 + } + ] } diff --git a/data/scenario_patches/7f38f1b.parkpatch b/data/scenario_patches/7f38f1b.parkpatch index a282000c56..5ab76eb98e 100644 --- a/data/scenario_patches/7f38f1b.parkpatch +++ b/data/scenario_patches/7f38f1b.parkpatch @@ -4,11 +4,11 @@ "land_ownership": { "available": { "coordinates": [ - [ 104, 190 ], [ 105, 190 ], [ 108, 197 ], - [ 75, 167 ], + [ 104, 190 ], [ 105, 190 ], [ 108, 197 ], + [ 75, 167 ], [ 61, 92 ], [ 61, 93 ], [ 61, 94 ], [ 61, 95 ], [ 62, 90 ], [ 62, 91 ], [ 62, 92 ], [ 62, 93 ], [ 62, 94 ], [ 92, 57 ], [ 93, 57 ], - [ 89, 40 ], [ 89, 41 ], [ 89, 42 ], [ 88, 42 ], + [ 89, 40 ], [ 89, 41 ], [ 89, 42 ], [ 88, 42 ], [ 168, 20 ], [ 169, 20 ], [ 46, 51 ], [ 58, 159 ], [ 71, 201 ], [ 126, 15 ], [ 190, 6 ] ] @@ -21,5 +21,12 @@ [ 103, 76 ], [ 104, 76 ] ] } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.bamboo_brown", + "surface": "rct2.footpath_surface.dirt", + "coordinates": [ [ 144, 100, 34 ] ] + } ] } diff --git a/data/scenario_patches/7ffdb44.parkpatch b/data/scenario_patches/7ffdb44.parkpatch index db6206427e..6fd885f4b6 100644 --- a/data/scenario_patches/7ffdb44.parkpatch +++ b/data/scenario_patches/7ffdb44.parkpatch @@ -4,11 +4,11 @@ "land_ownership": { "available": { "coordinates": [ - [ 104, 190 ], [ 105, 190 ], [ 108, 197 ], - [ 75, 167 ], + [ 104, 190 ], [ 105, 190 ], [ 108, 197 ], + [ 75, 167 ], [ 61, 92 ], [ 61, 93 ], [ 61, 94 ], [ 61, 95 ], [ 62, 90 ], [ 62, 91 ], [ 62, 92 ], [ 62, 93 ], [ 62, 94 ], [ 92, 57 ], [ 93, 57 ], - [ 89, 40 ], [ 89, 41 ], [ 89, 42 ], [ 88, 42 ], + [ 89, 40 ], [ 89, 41 ], [ 89, 42 ], [ 88, 42 ], [ 168, 20 ], [ 169, 20 ], [ 46, 51 ], [ 58, 159 ], [ 71, 201 ], [ 126, 15 ], [ 190, 6 ] ] @@ -21,5 +21,12 @@ [ 103, 76 ], [ 104, 76 ] ] } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.bamboo_brown", + "surface": "rct2.footpath_surface.dirt", + "coordinates": [ [ 144, 100, 34 ] ] + } ] } diff --git a/data/scenario_patches/b080197.parkpatch b/data/scenario_patches/b080197.parkpatch new file mode 100644 index 0000000000..642e69bf10 --- /dev/null +++ b/data/scenario_patches/b080197.parkpatch @@ -0,0 +1,21 @@ +{ + "scenario_name": "Mines of Africa (.sea)", + "sha256": "b080197d430a6ded46945035a95c8f2d9e3064637e9119e68270554eda48e747", + "elements_to_delete": [ + { + "element_index": 1, + "coordinates": [ + [ 43, 46 ] + ] + } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct2.footpath_surface.queue_yellow", + "coordinates": [ [ 43, 46, 6 ] ], + "slope_direction": 3, + "queue": true + } + ] +} diff --git a/data/scenario_patches/b20bd80.parkpatch b/data/scenario_patches/b20bd80.parkpatch new file mode 100644 index 0000000000..973c8e350e --- /dev/null +++ b/data/scenario_patches/b20bd80.parkpatch @@ -0,0 +1,40 @@ +{ + "scenario_name": "Iceberg Islands (.sea)", + "sha256": "b20bd803c0a99c8748b48688501ba6ef8a5ce5d746540a486617e0564aa407d5", + "elements_to_delete": [ + { + "element_index": 2, + "coordinates": [ + [ 49, 43 ], [ 50, 43 ], [ 51, 43 ], [ 44, 55 ] + ] + }, + { + "element_index": 1, + "coordinates": [ + [ 44, 54 ], [ 44, 53 ] + ] + } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct2.footpath_surface.queue_yellow", + "coordinates": [ [ 51, 43, 24 ], [ 44, 55, 20 ] ], + "queue": true + }, + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct2.footpath_surface.queue_yellow", + "coordinates": [ [ 50, 43, 24 ], [ 49, 43, 26 ] ], + "slope_direction": 0, + "queue": true + }, + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct2.footpath_surface.queue_yellow", + "coordinates": [ [ 44, 54, 18 ], [ 44, 53, 16 ] ], + "slope_direction": 1, + "queue": true + } + ] +} diff --git a/data/scenario_patches/c1d4056.parkpatch b/data/scenario_patches/c1d4056.parkpatch index 7f291735a1..96934e447d 100644 --- a/data/scenario_patches/c1d4056.parkpatch +++ b/data/scenario_patches/c1d4056.parkpatch @@ -4,11 +4,11 @@ "land_ownership": { "available": { "coordinates": [ - [ 104, 190 ], [ 105, 190 ], [ 108, 197 ], - [ 75, 167 ], + [ 104, 190 ], [ 105, 190 ], [ 108, 197 ], + [ 75, 167 ], [ 61, 92 ], [ 61, 93 ], [ 61, 94 ], [ 61, 95 ], [ 62, 90 ], [ 62, 91 ], [ 62, 92 ], [ 62, 93 ], [ 62, 94 ], [ 92, 57 ], [ 93, 57 ], - [ 89, 40 ], [ 89, 41 ], [ 89, 42 ], [ 88, 42 ], + [ 89, 40 ], [ 89, 41 ], [ 89, 42 ], [ 88, 42 ], [ 168, 20 ], [ 169, 20 ], [ 46, 51 ], [ 58, 159 ], [ 71, 201 ], [ 126, 15 ], [ 190, 6 ] ] @@ -21,5 +21,12 @@ [ 103, 76 ], [ 104, 76 ] ] } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.bamboo_brown", + "surface": "rct2.footpath_surface.dirt", + "coordinates": [ [ 144, 100, 34 ] ] + } ] } diff --git a/data/scenario_patches/c82272a.parkpatch b/data/scenario_patches/c82272a.parkpatch index a75e9e8fef..e220c663c5 100644 --- a/data/scenario_patches/c82272a.parkpatch +++ b/data/scenario_patches/c82272a.parkpatch @@ -4,11 +4,11 @@ "land_ownership": { "available": { "coordinates": [ - [ 104, 190 ], [ 105, 190 ], [ 108, 197 ], - [ 75, 167 ], + [ 104, 190 ], [ 105, 190 ], [ 108, 197 ], + [ 75, 167 ], [ 61, 92 ], [ 61, 93 ], [ 61, 94 ], [ 61, 95 ], [ 62, 90 ], [ 62, 91 ], [ 62, 92 ], [ 62, 93 ], [ 62, 94 ], [ 92, 57 ], [ 93, 57 ], - [ 89, 40 ], [ 89, 41 ], [ 89, 42 ], [ 88, 42 ], + [ 89, 40 ], [ 89, 41 ], [ 89, 42 ], [ 88, 42 ], [ 168, 20 ], [ 169, 20 ], [ 46, 51 ], [ 58, 159 ], [ 71, 201 ], [ 126, 15 ], [ 190, 6 ] ] @@ -21,5 +21,12 @@ [ 103, 76 ], [ 104, 76 ] ] } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.bamboo_brown", + "surface": "rct2.footpath_surface.dirt", + "coordinates": [ [ 144, 100, 34 ] ] + } ] } diff --git a/data/scenario_patches/eabcb3d.parkpatch b/data/scenario_patches/eabcb3d.parkpatch new file mode 100644 index 0000000000..e266f8fc1b --- /dev/null +++ b/data/scenario_patches/eabcb3d.parkpatch @@ -0,0 +1,31 @@ +{ + "scenario_name": "Rollercoaster Heaven (.sea)", + "sha256": "eabcb3d924e8e3438bfd90f6758a723c89df4136a817533d34985598d58ba38f", + "elements_to_delete": [ + { + "element_index": 0, + "coordinates": [ + [ 23, 79 ], [ 24, 79 ] + ] + }, + { + "element_index": 1, + "coordinates": [ + [ 87, 13 ] + ] + } + ], + "paths": [ + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct2.footpath_surface.crazy_paving", + "coordinates": [ [ 24, 79, 14 ], [ 87, 13, 18 ] ] + }, + { + "railings": "rct2.footpath_railings.concrete", + "surface": "rct2.footpath_surface.crazy_paving", + "coordinates": [ [ 23, 79, 12 ] ], + "slope_direction": 2 + } + ] +} diff --git a/data/scenario_patches/scenario_to_hash b/data/scenario_patches/scenario_to_hash index 6d352dc211..3ffa3ec789 100644 --- a/data/scenario_patches/scenario_to_hash +++ b/data/scenario_patches/scenario_to_hash @@ -39,6 +39,8 @@ 'fcc15f9c9b42bdd4aa8761c3a6df17c1293aa616780bc4aadd348d191e275112'], 'Lost City Founder': ['13e81f2', '13e81f23ab1a7051b5465e4a7bb214b4188f2264d499f8f7e106372c3a984331'], + 'Mines of Africa': ['2980c28', + '2980c287d81e4e96a46db30d1a80e6bfa0caace6a14488dddc6f20ac71676cff'], 'Mirage Madness': ['2696a05', '2696a059c2c1b23c60cbfcc293fd29cfec45d7e3da7f3b38bc2b52aff834fd34'], 'Mythological Madness': ['ef0c020', @@ -51,6 +53,8 @@ '33bac63d13aa7513ac8536d865cbc6fa4a2189c79e3943869e6380072e71bce7'], 'Rock ‘n’ Roll Revival': ['d48bbfe', 'd48bbfe4833347dfbf5befe63eb3795df3bce36cdc9152048ee7851e36d45ad9'], + 'Rollercoaster Heaven': ['0b8cc95', + '0b8cc952c399e1515546a4ababe5ba2f7aace83915a7e51c56ee901568c9ef56'], 'Schneider Shores': ['e57112f', 'e57112f58a7710d3e80242e867fb65d720e0cd3b67bebfd6b7df8b404fc7ea2b'], 'Sherwood Forest': ['825134a', @@ -87,6 +91,8 @@ '102a1c52853e77b6efd448a44572a862fa440615b4ea9ae5d7fb31c48c96aac9'], 'Hydro Hills': ['bfbd61f', 'bfbd61f6d22cdc5f2e017935f4e9e5969dece159218a08c695bfd727382cc717'], + 'Iceberg Islands': ['4a762ae', + '4a762ae89bf279dbcaead9e457c21014a9e39eb4a46cf1cbf63304450f9ed050'], 'katie\'s dreamland': ['73d0921', '73d0921f1d49388ffb4deb300c6ebb3920564410c2239580a7d1145fa54c2d4a'], 'Leafy Lake': ['83bd798', @@ -130,7 +136,9 @@ 'Haunted Harbour': ['b2cebe1', 'b2cebe149b2330e071a028d079f6632af144fb44e076a5eca89780eb4007e136'], 'hydro hills': ['ce24961', - 'ce249614e735c560c5019318bc6fa8603ba6630576f7fb038def0bdac3e143ef'] + 'ce249614e735c560c5019318bc6fa8603ba6630576f7fb038def0bdac3e143ef'], + 'Iceberg Islands': ['b20bd80', + 'b20bd803c0a99c8748b48688501ba6ef8a5ce5d746540a486617e0564aa407d5'], 'katie\'s dreamland': ['b8b572d', 'b8b572d394b145535cdb20f66b0bee9a497683a5885e4d78af6773c5bc0323ff'], 'mel\'s world': ['bfaf504', @@ -195,6 +203,8 @@ 'adffe2ff1e06ebeb821bbf01263125dc40311a8350722c62908be8d1c8852259'], 'Lost City Founder': ['70ce3e1', '70ce3e11f1dd59929a6d82d0f6f88897c95b94d1f9d4efd6ef3a0c6c449f966f'], + 'Mines of Africa': ['b080197', + 'b080197d430a6ded46945035a95c8f2d9e3064637e9119e68270554eda48e747'], 'Mirage Madness': ['82aeaf6', '82aeaf6bd628bf26dabd1ca87455b667f3081598bcf4d4c7751e2bdfbd266ac2'], 'Mythological Madness': ['6633d17', @@ -207,6 +217,8 @@ 'b43b07e47f2e6cb762a86760ac0242595617aa59bfd9811cec7e2dcc121ae367'], 'Rock ‘n’ Roll Revival': ['f71c978', 'f71c9788ab40ac591d5c96397fad8b12d9d3ac7830eac53f6ee5dc024c8c2bcf'], + 'Rollercoaster Heaven': ['eabcb3d', + 'eabcb3d924e8e3438bfd90f6758a723c89df4136a817533d34985598d58ba38f'], 'Schneider Shores': ['0d53bdc', '0d53bdc076d75d86b31b6b3e6948e3d45671cf5aeff6b2b3c07a7618923223f5'], 'Sherwood Forest': ['a04b536', @@ -220,4 +232,4 @@ 'Six Flags over Texas': ['6226822', '62268223a1539c92b7494973263457c9e9bf386c1e68eef21d377854f0ac0e38'], 'Wacky Waikiki': ['72cf3d2', - '72cf3d220740fd64f7681d3533320598cf6d3b71dff484bc43045e8d9d7a1a4b'], \ No newline at end of file + '72cf3d220740fd64f7681d3533320598cf6d3b71dff484bc43045e8d9d7a1a4b'], diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 65caffd754..627598ac12 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -6,6 +6,7 @@ - Improved: [#23123] Improve sorting of roller coasters in build new ride menu. - Improved: [#23211] Add boosters to classic wooden roller coaster (cheats only). - Improved: [#23233] Add diagonal booster to LSM Launched Coaster. +- Fix: [#20070, #22972] Missing and mismatched flat and sloped footpaths on several scenarios. - Fix: [#22726] ‘Force park rating’ cheat is not saved with the park. - Fix: [#23064] Stand-Up Roller Coaster unbanked to banked track pieces are misaligned. - Fix: [#23066] Stand-Up Roller Coaster has many supports that don't join up to the track. diff --git a/src/openrct2/rct12/ScenarioPatcher.cpp b/src/openrct2/rct12/ScenarioPatcher.cpp index 359d6eba8b..bb334a3072 100644 --- a/src/openrct2/rct12/ScenarioPatcher.cpp +++ b/src/openrct2/rct12/ScenarioPatcher.cpp @@ -12,6 +12,8 @@ #include "../Context.h" #include "../Game.h" #include "../PlatformEnvironment.h" +#include "../actions/FootpathPlaceAction.h" +#include "../actions/GameActionResult.h" #include "../core/File.h" #include "../core/Guard.hpp" #include "../core/Json.hpp" @@ -27,6 +29,8 @@ #include "../world/Location.hpp" #include "../world/Map.h" #include "../world/tile_element/EntranceElement.h" +#include "../world/tile_element/PathElement.h" +#include "../world/tile_element/Slope.h" #include "../world/tile_element/SurfaceElement.h" #include "../world/tile_element/TileElement.h" #include "../world/tile_element/TileElementType.h" @@ -74,6 +78,13 @@ static const std::string _ridesKey = "rides"; static const std::string _rideIdKey = "id"; static const std::string _operationKey = "operation"; +// Path fix keys +static const std::string _pathsKey = "paths"; +static const std::string _railingsKey = "railings"; +static const std::string _surfaceKey = "surface"; +static const std::string _directionKey = "slope_direction"; +static const std::string _isQueue = "queue"; + static u8string ToOwnershipJsonKey(int ownershipType) { switch (ownershipType) @@ -93,7 +104,33 @@ static u8string ToOwnershipJsonKey(int ownershipType) return {}; } -static std::vector getCoordinates(const json_t& parameters) +static void readCoordinate(std::vector& out, const json_t& coordinatesArray) +{ + if (coordinatesArray.size() != 2) + { + OpenRCT2::Guard::Assert(0, "Fix coordinates sub array should have 2 elements"); + return; + } + + out.emplace_back( + OpenRCT2::Json::GetNumber(coordinatesArray[0]), OpenRCT2::Json::GetNumber(coordinatesArray[1])); +} + +static void readCoordinate(std::vector& out, const json_t& coordinatesArray) +{ + if (coordinatesArray.size() != 3) + { + OpenRCT2::Guard::Assert(0, "Fix coordinates sub array should have 3 elements"); + return; + } + + out.emplace_back( + OpenRCT2::Json::GetNumber(coordinatesArray[0]), OpenRCT2::Json::GetNumber(coordinatesArray[1]), + OpenRCT2::Json::GetNumber(coordinatesArray[2])); +} + +template +static std::vector getCoordinates(const json_t& parameters) { if (!parameters.contains(_coordinatesKey)) { @@ -113,7 +150,7 @@ static std::vector getCoordinates(const json_t& parameters) return {}; } - std::vector parsedCoordinates; + std::vector parsedCoordinates; parsedCoordinates.reserve(coords.size()); for (size_t i = 0; i < coords.size(); ++i) { @@ -123,18 +160,52 @@ static std::vector getCoordinates(const json_t& parameters) return {}; } - auto coordinatesPair = OpenRCT2::Json::AsArray(coords[i]); - if (coordinatesPair.size() != 2) - { - OpenRCT2::Guard::Assert(0, "Fix coordinates sub array should have 2 elements"); - return {}; - } - parsedCoordinates.emplace_back( - OpenRCT2::Json::GetNumber(coordinatesPair[0]), OpenRCT2::Json::GetNumber(coordinatesPair[1])); + auto coordinatesArray = OpenRCT2::Json::AsArray(coords[i]); + readCoordinate(parsedCoordinates, coordinatesArray); } return parsedCoordinates; } +static Direction GetDirection(const json_t& parameters) +{ + if (!parameters.contains(_directionKey)) + { + return INVALID_DIRECTION; + } + else if (!parameters[_directionKey].is_number()) + { + OpenRCT2::Guard::Assert(0, "Fix direction must be a number"); + return INVALID_DIRECTION; + } + + Direction direction = OpenRCT2::Json::GetNumber(parameters[_directionKey]); + + if (direction > 3) + { + OpenRCT2::Guard::Assert(0, "Direction must be between 0 and 3"); + return INVALID_DIRECTION; + } + + return direction; +} + +static bool IsQueue(const json_t& parameters) +{ + if (!parameters.contains(_isQueue)) + { + return false; + } + else if (!parameters[_isQueue].is_boolean()) + { + OpenRCT2::Guard::Assert(0, "queue must be a boolean"); + return false; + } + else + { + return OpenRCT2::Json::GetBoolean(parameters[_isQueue]); + } +} + static void ApplyLandOwnershipFixes(const json_t& landOwnershipFixes, int ownershipType) { auto ownershipTypeKey = ToOwnershipJsonKey(ownershipType); @@ -517,6 +588,84 @@ static void ApplyRideFixes(const json_t& scenarioPatch) } } +static void ApplyPathFixes(const json_t& scenarioPatch) +{ + if (!scenarioPatch.contains(_pathsKey)) + { + return; + } + + if (!scenarioPatch[_pathsKey].is_array()) + { + OpenRCT2::Guard::Assert(0, "Path fixes should be an array of arrays"); + return; + } + + auto pathFixes = OpenRCT2::Json::AsArray(scenarioPatch[_pathsKey]); + if (pathFixes.empty()) + { + OpenRCT2::Guard::Assert(0, "Path fixes should not be an empty array"); + return; + } + + for (size_t i = 0; i < pathFixes.size(); ++i) + { + auto pathFix = pathFixes[i]; + + if (!pathFix.contains(_railingsKey)) + { + OpenRCT2::Guard::Assert(0, "Path fixes should have railings"); + return; + } + + if (!pathFix.contains(_surfaceKey)) + { + OpenRCT2::Guard::Assert(0, "Path fixes should have a surface"); + return; + } + + auto railings = OpenRCT2::Json::GetString(pathFix[_railingsKey]); + auto surface = OpenRCT2::Json::GetString(pathFix[_surfaceKey]); + + if (_dryRun) + { + continue; + } + + auto& objectManager = OpenRCT2::GetContext()->GetObjectManager(); + auto railingsObjIndex = objectManager.GetLoadedObjectEntryIndex(railings); + auto surfaceObjIndex = objectManager.GetLoadedObjectEntryIndex(surface); + + if (railingsObjIndex == OBJECT_ENTRY_INDEX_NULL) + { + OpenRCT2::Guard::Assert(0, "Railings object not found"); + return; + } + + if (surfaceObjIndex == OBJECT_ENTRY_INDEX_NULL) + { + OpenRCT2::Guard::Assert(0, "Surface object not found"); + return; + } + + auto coordinates = getCoordinates(pathFix); + Direction direction = GetDirection(pathFix); + PathConstructFlags constructionFlags = IsQueue(pathFix) ? OpenRCT2::PathConstructFlag::IsQueue : 0; + + for (auto coordinate : coordinates) + { + auto slope = direction != INVALID_DIRECTION ? direction + 4 : 0; + auto footpathPlaceAction = FootpathPlaceAction( + coordinate.ToCoordsXYZ(), slope, surfaceObjIndex, railingsObjIndex, direction, constructionFlags); + auto result = footpathPlaceAction.Execute(); + if (result.Error != OpenRCT2::GameActions::Status::Ok) + { + OpenRCT2::Guard::Assert(0, "Could not patch path"); + } + } + } +} + static u8string getScenarioSHA256(u8string_view scenarioPath) { auto env = OpenRCT2::GetContext()->GetPlatformEnvironment(); @@ -581,6 +730,7 @@ void OpenRCT2::RCT12::ApplyScenarioPatch(u8string_view scenarioPatchFile, u8stri ApplySurfaceFixes(scenarioPatch); RemoveTileElements(scenarioPatch); ApplyRideFixes(scenarioPatch); + ApplyPathFixes(scenarioPatch); } void OpenRCT2::RCT12::FetchAndApplyScenarioPatch(u8string_view scenarioPath)