mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-16 03:23:15 +01:00
Merge pull request #17929 from IntelOrca/rctc-support
Add support for RCT Classic assets
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<li>Auto-saving and giant screenshots.</li>
|
||||
</ul>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</description>
|
||||
<screenshots>
|
||||
|
||||
@@ -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<size_t>(did)];
|
||||
break;
|
||||
case DIRBASE::RCT2:
|
||||
directoryName = _usingRctClassic ? "Assets" : DirectoryNamesRCT2[static_cast<size_t>(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<size_t>(base)] = path;
|
||||
|
||||
if (base == DIRBASE::RCT2)
|
||||
{
|
||||
_usingRctClassic = Platform::IsRCTClassicPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsUsingClassic() const override
|
||||
{
|
||||
return _usingRctClassic;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -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<IPlatformEnvironment> CreatePlatformEnvironment(DIRBASE_VALUES basePaths);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<rct_g1_header>();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -502,7 +502,10 @@ namespace ObjectFactory
|
||||
std::unique_ptr<Object> 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(...)");
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user