diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 69a8b73ebd..1d7db9c279 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -3496,8 +3496,8 @@ STR_6386 :Blizzard STR_6387 :Can’t lower element here… STR_6388 :Can’t raise element here… STR_6389 :Invalid clearance -STR_6390 :OpenRCT2 needs files from the original RollerCoaster Tycoon 2 in order to work. Please select the directory where you installed RollerCoaster Tycoon 2. -STR_6391 :Please select your RCT2 directory +STR_6390 :OpenRCT2 needs files from the original RollerCoaster Tycoon 2 or RollerCoaster Tycoon Classic in order to work. Please select the directory where you installed RollerCoaster Tycoon 2 or RollerCoaster Tycoon Classic. +STR_6391 :Please select your RCT2 or RCTC directory STR_6392 :Could not find {STRING} at this path. STR_6393 :Objective Selection STR_6394 :Objective @@ -3505,9 +3505,9 @@ STR_6395 :Maintenance STR_6396 :Disable screensaver and monitor power saving STR_6397 :If checked, screensaver and other monitor power saving features will be inhibited while OpenRCT2 is running. STR_6398 :File contains unsupported ride types. Please update to a newer version of OpenRCT2. -STR_6399 :OpenRCT2 needs files from the original RollerCoaster Tycoon 2 in order to work. Please set the “game_path” variable in config.ini to the directory where you installed RollerCoaster Tycoon 2, then restart OpenRCT2. +STR_6399 :OpenRCT2 needs files from the original RollerCoaster Tycoon 2 or RollerCoaster Tycoon Classic in order to work. Please set the “game_path” variable in config.ini to the directory where you installed RollerCoaster Tycoon 2 or RollerCoaster Tycoon Classic, then restart OpenRCT2. STR_6400 :I have the GOG offline installer for RollerCoaster Tycoon 2 downloaded, but it is not installed -STR_6401 :I have RollerCoaster Tycoon 2 installed already +STR_6401 :I have RollerCoaster Tycoon 2 or RollerCoaster Tycoon Classic installed already STR_6402 :OpenRCT2 Data Setup STR_6403 :Select which applies best to you STR_6404 :Please select the GOG RollerCoaster Tycoon 2 installer. diff --git a/distribution/changelog.txt b/distribution/changelog.txt index ea2d89364e..4139369402 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -8,6 +8,7 @@ - Feature: [#17638] Added Zero G rolls, medium loops and large corkscrews to the Hybrid and Single-Rail coasters. - Feature: [#17877] Add three real-life flying roller coaster colour schemes. - Feature: [#17900] Add “Classic Wooden Coaster” with shallow banked turns. +- Feature: [#17929] Fully support RollerCoaster Tycoon Classic as a RCT2 base install path. - Feature: [objects#198] Add additional pirate roofs. - Improved: [#15358] Park and scenario names can now contain up to 128 characters. - Improved: [#16840] Add support for rectangular heightmaps. diff --git a/distribution/linux/openrct2.appdata.xml b/distribution/linux/openrct2.appdata.xml index 990b9f3799..24dde734ef 100644 --- a/distribution/linux/openrct2.appdata.xml +++ b/distribution/linux/openrct2.appdata.xml @@ -30,7 +30,7 @@
  • Auto-saving and giant screenshots.
  • - Original RollerCoaster Tycoon 2 game files are required in order to play OpenRCT2. + Original RollerCoaster Tycoon 2 or RollerCoaster Tycoon Classic game files are required in order to play OpenRCT2.

    diff --git a/src/openrct2/PlatformEnvironment.cpp b/src/openrct2/PlatformEnvironment.cpp index 6e42dd791b..801773d96a 100644 --- a/src/openrct2/PlatformEnvironment.cpp +++ b/src/openrct2/PlatformEnvironment.cpp @@ -22,6 +22,7 @@ class PlatformEnvironment final : public IPlatformEnvironment { private: u8string _basePath[DIRBASE_COUNT]; + bool _usingRctClassic{}; public: explicit PlatformEnvironment(DIRBASE_VALUES basePaths) @@ -45,9 +46,11 @@ public: { default: case DIRBASE::RCT1: - case DIRBASE::RCT2: directoryName = DirectoryNamesRCT2[static_cast(did)]; break; + case DIRBASE::RCT2: + directoryName = _usingRctClassic ? "Assets" : DirectoryNamesRCT2[static_cast(did)]; + break; case DIRBASE::OPENRCT2: case DIRBASE::USER: case DIRBASE::CONFIG: @@ -69,6 +72,19 @@ public: u8string FindFile(DIRBASE base, DIRID did, u8string_view fileName) const override { auto dataPath = GetDirectoryPath(base, did); + + std::string alternativeFilename; + if (_usingRctClassic && base == DIRBASE::RCT2 && did == DIRID::DATA) + { + // Special case, handle RCT Classic css ogg files + if (String::StartsWith(fileName, "css", true) && String::EndsWith(fileName, ".dat", true)) + { + alternativeFilename = fileName.substr(0, fileName.size() - 3); + alternativeFilename.append("ogg"); + fileName = alternativeFilename; + } + } + auto path = Path::ResolveCasing(Path::Combine(dataPath, fileName)); if (base == DIRBASE::RCT1 && did == DIRID::DATA && !File::Exists(path)) { @@ -80,12 +96,23 @@ public: path = alternativePath; } } + return path; } void SetBasePath(DIRBASE base, u8string_view path) override { _basePath[static_cast(base)] = path; + + if (base == DIRBASE::RCT2) + { + _usingRctClassic = Platform::IsRCTClassicPath(path); + } + } + + bool IsUsingClassic() const override + { + return _usingRctClassic; } private: diff --git a/src/openrct2/PlatformEnvironment.h b/src/openrct2/PlatformEnvironment.h index 58a68deb92..8d97ca9ddd 100644 --- a/src/openrct2/PlatformEnvironment.h +++ b/src/openrct2/PlatformEnvironment.h @@ -83,6 +83,7 @@ namespace OpenRCT2 virtual u8string GetFilePath(PATHID pathid) const abstract; virtual u8string FindFile(DIRBASE base, DIRID did, u8string_view fileName) const abstract; virtual void SetBasePath(DIRBASE base, u8string_view path) abstract; + virtual bool IsUsingClassic() const abstract; }; [[nodiscard]] std::unique_ptr CreatePlatformEnvironment(DIRBASE_VALUES basePaths); diff --git a/src/openrct2/audio/Audio.cpp b/src/openrct2/audio/Audio.cpp index 4cd5adcafd..ca6933996a 100644 --- a/src/openrct2/audio/Audio.cpp +++ b/src/openrct2/audio/Audio.cpp @@ -12,6 +12,7 @@ #include "../Context.h" #include "../Intro.h" #include "../OpenRCT2.h" +#include "../PlatformEnvironment.h" #include "../config/Config.h" #include "../core/File.h" #include "../core/FileStream.h" @@ -98,11 +99,31 @@ namespace OpenRCT2::Audio void LoadAudioObjects() { auto& objManager = GetContext()->GetObjectManager(); - auto* baseAudio = objManager.LoadObject(AudioObjectIdentifiers::Rct2Base); - if (baseAudio != nullptr) + + Object* baseAudio{}; + + // We have a different audio object for RCT Classic + auto env = GetContext()->GetPlatformEnvironment(); + if (env->IsUsingClassic()) { - _soundsAudioObjectEntryIndex = objManager.GetLoadedObjectEntryIndex(baseAudio); + baseAudio = objManager.LoadObject(AudioObjectIdentifiers::Rct2cBase); + if (baseAudio != nullptr) + { + baseAudio->SetIdentifier(AudioObjectIdentifiers::Rct2Base); + _soundsAudioObjectEntryIndex = objManager.GetLoadedObjectEntryIndex(baseAudio); + } } + + if (baseAudio == nullptr) + { + // Fallback to vanilla RCT2 audio object + baseAudio = objManager.LoadObject(AudioObjectIdentifiers::Rct2Base); + if (baseAudio != nullptr) + { + _soundsAudioObjectEntryIndex = objManager.GetLoadedObjectEntryIndex(baseAudio); + } + } + objManager.LoadObject(AudioObjectIdentifiers::Rct2Circus); } diff --git a/src/openrct2/audio/audio.h b/src/openrct2/audio/audio.h index 302e51b98a..bf6ce96575 100644 --- a/src/openrct2/audio/audio.h +++ b/src/openrct2/audio/audio.h @@ -137,6 +137,7 @@ namespace OpenRCT2::Audio { constexpr std::string_view Rct1Title = "rct1.audio.title"; constexpr std::string_view Rct2Base = "rct2.audio.base"; + constexpr std::string_view Rct2cBase = "rct2.audio.base.rctc"; constexpr std::string_view Rct2Title = "rct2.audio.title"; constexpr std::string_view Rct2Circus = "rct2.audio.circus"; } // namespace AudioObjectIdentifiers diff --git a/src/openrct2/drawing/Drawing.Sprite.cpp b/src/openrct2/drawing/Drawing.Sprite.cpp index 1c30498000..f9673ea1a0 100644 --- a/src/openrct2/drawing/Drawing.Sprite.cpp +++ b/src/openrct2/drawing/Drawing.Sprite.cpp @@ -199,7 +199,7 @@ bool gfx_load_g1(const IPlatformEnvironment& env) log_verbose("gfx_load_g1(...)"); try { - auto path = Path::Combine(env.GetDirectoryPath(DIRBASE::RCT2, DIRID::DATA), u8"g1.dat"); + auto path = env.FindFile(DIRBASE::RCT2, DIRID::DATA, u8"g1.dat"); auto fs = FileStream(path, FILE_MODE_OPEN); _g1.header = fs.ReadValue(); diff --git a/src/openrct2/object/MusicObject.cpp b/src/openrct2/object/MusicObject.cpp index c0e9ef215d..fd76562abe 100644 --- a/src/openrct2/object/MusicObject.cpp +++ b/src/openrct2/object/MusicObject.cpp @@ -185,9 +185,8 @@ ObjectAsset MusicObject::GetAsset(IReadObjectContext& context, std::string_view { if (path.find("$RCT2:DATA/") == 0) { - auto platformEnvironment = GetContext()->GetPlatformEnvironment(); - auto dir = platformEnvironment->GetDirectoryPath(DIRBASE::RCT2, DIRID::DATA); - auto path2 = Path::Combine(dir, std::string(path.substr(11))); + auto env = GetContext()->GetPlatformEnvironment(); + auto path2 = env->FindFile(DIRBASE::RCT2, DIRID::DATA, path.substr(11)); return ObjectAsset(path2); } diff --git a/src/openrct2/object/ObjectFactory.cpp b/src/openrct2/object/ObjectFactory.cpp index 76f21d88bf..cd7f99be58 100644 --- a/src/openrct2/object/ObjectFactory.cpp +++ b/src/openrct2/object/ObjectFactory.cpp @@ -502,7 +502,10 @@ namespace ObjectFactory std::unique_ptr CreateObjectFromJson( IObjectRepository& objectRepository, json_t& jRoot, const IFileDataRetriever* fileRetriever, bool loadImageTable) { - Guard::Assert(jRoot.is_object(), "ObjectFactory::CreateObjectFromJson expects parameter jRoot to be object"); + if (!jRoot.is_object()) + { + throw std::runtime_error("Object JSON root was not an object"); + } log_verbose("CreateObjectFromJson(...)"); diff --git a/src/openrct2/platform/Platform.h b/src/openrct2/platform/Platform.h index 0a827fcd7d..a2071e882d 100644 --- a/src/openrct2/platform/Platform.h +++ b/src/openrct2/platform/Platform.h @@ -87,6 +87,8 @@ namespace Platform bool ProcessIsElevated(); float GetDefaultScale(); + bool IsRCT2Path(std::string_view path); + bool IsRCTClassicPath(std::string_view path); bool OriginalGameDataExists(std::string_view path); std::string GetUsername(); diff --git a/src/openrct2/platform/Shared.cpp b/src/openrct2/platform/Shared.cpp index e64f0ca90d..25bddf8375 100644 --- a/src/openrct2/platform/Shared.cpp +++ b/src/openrct2/platform/Shared.cpp @@ -94,10 +94,21 @@ namespace Platform return outTime; } + bool IsRCT2Path(std::string_view path) + { + auto combinedPath = Path::ResolveCasing(Path::Combine(path, u8"Data", u8"g1.dat")); + return File::Exists(combinedPath); + } + + bool IsRCTClassicPath(std::string_view path) + { + auto combinedPath = Path::ResolveCasing(Path::Combine(path, u8"Assets", u8"g1.dat")); + return File::Exists(combinedPath); + } + bool OriginalGameDataExists(std::string_view path) { - std::string combinedPath = Path::ResolveCasing(Path::Combine(path, u8"Data", u8"g1.dat")); - return File::Exists(combinedPath); + return IsRCT2Path(path) || IsRCTClassicPath(path); } std::string SanitiseFilename(std::string_view originalName)