OpenRCT2/src/openrct2/Context.cpp

1566 lines
50 KiB
C++
Raw Normal View History

/*****************************************************************************
2020-07-21 15:04:34 +02:00
* Copyright (c) 2014-2020 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.
*****************************************************************************/
2017-09-04 23:17:35 +02:00
#ifdef __EMSCRIPTEN__
2018-07-21 16:17:06 +02:00
# include <emscripten.h>
2017-09-04 23:17:35 +02:00
#endif // __EMSCRIPTEN__
2018-11-21 23:16:04 +01:00
#include "Context.h"
2018-06-22 23:25:16 +02:00
#include "Editor.h"
#include "FileClassifier.h"
#include "Game.h"
#include "GameState.h"
#include "GameStateSnapshots.h"
2018-06-22 23:25:16 +02:00
#include "Input.h"
#include "Intro.h"
#include "OpenRCT2.h"
#include "ParkImporter.h"
#include "PlatformEnvironment.h"
#include "ReplayManager.h"
2018-06-22 23:25:16 +02:00
#include "Version.h"
#include "actions/GameAction.h"
2018-06-22 23:25:16 +02:00
#include "audio/AudioContext.h"
#include "audio/audio.h"
#include "config/Config.h"
#include "core/Console.hpp"
#include "core/File.h"
#include "core/FileScanner.h"
#include "core/FileStream.h"
#include "core/Guard.hpp"
#include "core/Http.h"
#include "core/MemoryStream.h"
#include "core/Path.hpp"
#include "core/String.hpp"
#include "drawing/IDrawingEngine.h"
2018-06-22 23:25:16 +02:00
#include "drawing/LightFX.h"
#include "interface/Chat.h"
#include "interface/InteractiveConsole.h"
#include "interface/Viewport.h"
#include "localisation/Date.h"
#include "localisation/Localisation.h"
2018-06-22 23:25:16 +02:00
#include "localisation/LocalisationService.h"
#include "network/DiscordService.h"
2021-08-17 05:20:07 +02:00
#include "network/NetworkBase.h"
#include "network/network.h"
#include "object/ObjectManager.h"
#include "object/ObjectRepository.h"
#include "paint/Painter.h"
2018-01-18 13:55:34 +01:00
#include "platform/Crash.h"
#include "platform/Platform2.h"
2018-06-22 23:25:16 +02:00
#include "platform/platform.h"
#include "ride/TrackData.h"
#include "ride/TrackDesignRepository.h"
2018-03-19 23:28:40 +01:00
#include "scenario/Scenario.h"
#include "scenario/ScenarioRepository.h"
2018-03-19 00:35:58 +01:00
#include "scripting/HookEngine.h"
#include "scripting/ScriptEngine.h"
#include "title/TitleScreen.h"
#include "title/TitleSequenceManager.h"
2018-06-22 23:25:16 +02:00
#include "ui/UiContext.h"
#include "ui/WindowManager.h"
2017-12-13 13:02:24 +01:00
#include "util/Util.h"
#include "world/EntityTweener.h"
2018-06-22 23:25:16 +02:00
#include "world/Park.h"
2021-02-23 00:28:57 +01:00
#include "world/Sprite.h"
2018-11-21 23:16:04 +01:00
#include <algorithm>
#include <cmath>
#include <exception>
#include <future>
2018-11-21 23:16:04 +01:00
#include <iterator>
#include <memory>
#include <string>
using namespace OpenRCT2;
2017-03-28 20:58:15 +02:00
using namespace OpenRCT2::Audio;
using namespace OpenRCT2::Drawing;
2018-04-24 14:18:05 +02:00
using namespace OpenRCT2::Localisation;
using namespace OpenRCT2::Paint;
using namespace OpenRCT2::Scripting;
using namespace OpenRCT2::Ui;
namespace OpenRCT2
{
class Context final : public IContext
{
private:
// Dependencies
std::shared_ptr<IPlatformEnvironment> const _env;
std::shared_ptr<IAudioContext> const _audioContext;
std::shared_ptr<IUiContext> const _uiContext;
// Services
std::unique_ptr<LocalisationService> _localisationService;
std::unique_ptr<IObjectRepository> _objectRepository;
std::unique_ptr<IObjectManager> _objectManager;
std::unique_ptr<ITrackDesignRepository> _trackDesignRepository;
std::unique_ptr<IScenarioRepository> _scenarioRepository;
std::unique_ptr<IReplayManager> _replayManager;
std::unique_ptr<IGameStateSnapshots> _gameStateSnapshots;
2017-11-10 21:23:23 +01:00
#ifdef __ENABLE_DISCORD__
std::unique_ptr<DiscordService> _discordService;
2017-11-10 21:23:23 +01:00
#endif
2018-06-22 23:25:16 +02:00
StdInOutConsole _stdInOutConsole;
#ifdef ENABLE_SCRIPTING
ScriptEngine _scriptEngine;
2020-02-23 13:55:48 +01:00
#endif
2021-08-17 05:20:07 +02:00
#ifndef DISABLE_NETWORK
NetworkBase _network;
#endif
2017-07-23 00:42:14 +02:00
// Game states
std::unique_ptr<TitleScreen> _titleScreen;
std::unique_ptr<GameState> _gameState;
2017-07-23 00:42:14 +02:00
DrawingEngine _drawingEngineType = DrawingEngine::Software;
std::unique_ptr<IDrawingEngine> _drawingEngine;
std::unique_ptr<Painter> _painter;
2018-06-22 23:25:16 +02:00
bool _initialised = false;
bool _isWindowMinimised = false;
uint32_t _lastTick = 0;
float _accumulator = 0.0f;
float _timeScale = 1.0f;
uint32_t _lastUpdateTime = 0;
2018-06-22 23:25:16 +02:00
bool _variableFrame = false;
// If set, will end the OpenRCT2 game loop. Intentionally private to this module so that the flag can not be set back to
// false.
bool _finished = false;
std::future<void> _versionCheckFuture;
NewVersionInfo _newVersionInfo;
bool _hasNewVersionInfo = false;
public:
// Singleton of Context.
// Remove this when GetContext() is no longer called so that
// multiple instances can be created in parallel
2018-06-22 23:25:16 +02:00
static Context* Instance;
public:
Context(
const std::shared_ptr<IPlatformEnvironment>& env, const std::shared_ptr<IAudioContext>& audioContext,
2018-04-27 19:47:57 +02:00
const std::shared_ptr<IUiContext>& uiContext)
2018-06-22 23:25:16 +02:00
: _env(env)
, _audioContext(audioContext)
, _uiContext(uiContext)
, _localisationService(std::make_unique<LocalisationService>(env))
#ifdef ENABLE_SCRIPTING
, _scriptEngine(_stdInOutConsole, *env)
2021-08-17 05:20:07 +02:00
#endif
#ifndef DISABLE_NETWORK
, _network(*this)
2020-02-23 13:55:48 +01:00
#endif
2020-02-22 19:04:27 +01:00
, _painter(std::make_unique<Painter>(uiContext))
{
// Can't have more than one context currently.
Guard::Assert(Instance == nullptr);
Instance = this;
}
~Context() override
{
2018-12-17 12:58:12 +01:00
// NOTE: We must shutdown all systems here before Instance is set back to null.
// If objects use GetContext() in their destructor things won't go well.
GameActions::ClearQueue();
2021-08-17 09:36:18 +02:00
#ifndef DISABLE_NETWORK
2021-08-17 05:20:07 +02:00
_network.Close();
2021-08-17 09:36:18 +02:00
#endif
window_close_all();
// Unload objects after closing all windows, this is to overcome windows like
// the object selection window which loads objects when closed.
if (_objectManager != nullptr)
{
_objectManager->UnloadAll();
}
gfx_object_check_all_images_freed();
2021-03-30 22:49:00 +02:00
gfx_unload_csg();
gfx_unload_g2();
gfx_unload_g1();
Audio::Close();
config_release();
Instance = nullptr;
}
std::shared_ptr<IAudioContext> GetAudioContext() override
2017-03-28 20:58:15 +02:00
{
return _audioContext;
2017-03-28 20:58:15 +02:00
}
std::shared_ptr<IUiContext> GetUiContext() override
{
return _uiContext;
}
#ifdef ENABLE_SCRIPTING
Scripting::ScriptEngine& GetScriptEngine() override
{
return _scriptEngine;
}
2020-02-23 13:55:48 +01:00
#endif
2018-06-22 23:25:16 +02:00
GameState* GetGameState() override
{
return _gameState.get();
}
std::shared_ptr<IPlatformEnvironment> GetPlatformEnvironment() override
{
return _env;
}
Localisation::LocalisationService& GetLocalisationService() override
2018-04-24 14:18:05 +02:00
{
return *_localisationService;
2018-04-24 14:18:05 +02:00
}
IObjectManager& GetObjectManager() override
{
return *_objectManager;
}
IObjectRepository& GetObjectRepository() override
{
return *_objectRepository;
}
2018-06-22 23:25:16 +02:00
ITrackDesignRepository* GetTrackDesignRepository() override
{
return _trackDesignRepository.get();
}
2018-06-22 23:25:16 +02:00
IScenarioRepository* GetScenarioRepository() override
{
return _scenarioRepository.get();
}
IReplayManager* GetReplayManager() override
{
return _replayManager.get();
}
IGameStateSnapshots* GetGameStateSnapshots() override
{
return _gameStateSnapshots.get();
}
DrawingEngine GetDrawingEngineType() override
{
return _drawingEngineType;
}
2018-06-22 23:25:16 +02:00
IDrawingEngine* GetDrawingEngine() override
{
return _drawingEngine.get();
}
2021-08-17 05:20:07 +02:00
Paint::Painter* GetPainter() override
2019-02-19 16:03:08 +01:00
{
return _painter.get();
}
2021-08-17 05:20:07 +02:00
#ifndef DISABLE_NETWORK
NetworkBase& GetNetwork() override
{
return _network;
}
#endif
2018-06-22 23:25:16 +02:00
int32_t RunOpenRCT2(int argc, const char** argv) override
{
if (Initialise())
{
Launch();
2019-10-05 18:57:07 +02:00
return EXIT_SUCCESS;
}
2019-10-05 18:57:07 +02:00
return EXIT_FAILURE;
}
2018-06-22 23:25:16 +02:00
void WriteLine(const std::string& s) override
2018-03-11 23:25:34 +01:00
{
_stdInOutConsole.WriteLine(s);
}
2021-02-13 01:15:27 +01:00
void WriteErrorLine(const std::string& s) override
{
_stdInOutConsole.WriteLineError(s);
}
/**
* Causes the OpenRCT2 game loop to finish.
*/
2017-05-06 22:53:51 +02:00
void Finish() override
{
_finished = true;
}
2017-07-28 19:43:48 +02:00
void Quit() override
{
gSavePromptMode = PromptMode::Quit;
2017-08-06 05:22:00 +02:00
context_open_window(WC_SAVE_PROMPT);
2017-07-28 19:43:48 +02:00
}
std::string GetPathLegacy(int32_t pathId) override
{
static constexpr const char* const LegacyFileNames[PATH_ID_END] = {
nullptr, nullptr, "css1.dat", "css2.dat", "css4.dat", "css5.dat", "css6.dat", "css7.dat",
"css8.dat", "css9.dat", "css11.dat", "css12.dat", "css13.dat", "css14.dat", "css15.dat", "css3.dat",
"css17.dat", "css18.dat", "css19.dat", "css20.dat", "css21.dat", "css22.dat", nullptr, "css23.dat",
"css24.dat", "css25.dat", "css26.dat", "css27.dat", "css28.dat", "css29.dat", "css30.dat", "css31.dat",
"css32.dat", "css33.dat", "css34.dat", "css35.dat", "css36.dat", "css37.dat", "css38.dat", "CUSTOM1.WAV",
"CUSTOM2.WAV", "css39.dat", "css40.dat", "css41.dat", nullptr, "css42.dat", "css43.dat", "css44.dat",
"css45.dat", "css46.dat", "css50.dat",
};
std::string result;
if (pathId == PATH_ID_CSS50)
{
if (!(_env->GetDirectoryPath(DIRBASE::RCT1).empty()))
{
auto dataPath = _env->GetDirectoryPath(DIRBASE::RCT1, DIRID::DATA);
result = Path::ResolveCasing(Path::Combine(dataPath, "css17.dat"));
if (!File::Exists(result))
{
auto rct1Path = _env->GetDirectoryPath(DIRBASE::RCT1);
result = Path::ResolveCasing(Path::Combine(rct1Path, "RCTdeluxe_install", "Data", "css17.dat"));
}
}
else
{
auto dataPath = _env->GetDirectoryPath(DIRBASE::RCT2, DIRID::DATA);
result = Path::ResolveCasing(Path::Combine(dataPath, "css50.dat"));
}
}
else if (pathId >= 0 && pathId < PATH_ID_END)
{
auto fileName = LegacyFileNames[pathId];
if (fileName != nullptr)
{
auto dataPath = _env->GetDirectoryPath(DIRBASE::RCT2, DIRID::DATA);
result = Path::Combine(dataPath, fileName);
}
}
return result;
}
bool Initialise() final override
{
if (_initialised)
{
throw std::runtime_error("Context already initialised.");
}
_initialised = true;
crash_init();
if (gConfigGeneral.last_run_version != nullptr && String::Equals(gConfigGeneral.last_run_version, OPENRCT2_VERSION))
{
gOpenRCT2ShowChangelog = false;
}
else
{
gOpenRCT2ShowChangelog = true;
gConfigGeneral.last_run_version = String::Duplicate(OPENRCT2_VERSION);
config_save_default();
}
try
{
_localisationService->OpenLanguage(gConfigGeneral.language);
}
catch (const std::exception& e)
{
log_error("Failed to open configured language: %s", e.what());
try
{
_localisationService->OpenLanguage(LANGUAGE_ENGLISH_UK);
}
catch (const std::exception& eFallback)
{
log_fatal("Failed to open fallback language: %s", eFallback.what());
auto uiContext = GetContext()->GetUiContext();
uiContext->ShowMessageBox("Failed to load language file!\nYour installation may be damaged.");
return false;
}
}
// TODO add configuration option to allow multiple instances
// if (!gOpenRCT2Headless && !platform_lock_single_instance()) {
// log_fatal("OpenRCT2 is already running.");
// return false;
// } //This comment was relocated so it would stay where it was in relation to the following lines of code.
if (!gOpenRCT2Headless)
{
auto rct2InstallPath = GetOrPromptRCT2Path();
if (rct2InstallPath.empty())
{
return false;
}
_env->SetBasePath(DIRBASE::RCT2, rct2InstallPath);
}
TrackMetaData::Init();
_objectRepository = CreateObjectRepository(_env);
_objectManager = CreateObjectManager(*_objectRepository);
_trackDesignRepository = CreateTrackDesignRepository(_env);
_scenarioRepository = CreateScenarioRepository(_env);
_replayManager = CreateReplayManager();
_gameStateSnapshots = CreateGameStateSnapshots();
2017-11-10 21:23:23 +01:00
#ifdef __ENABLE_DISCORD__
if (!gOpenRCT2Headless)
{
_discordService = std::make_unique<DiscordService>();
}
2017-11-10 21:23:23 +01:00
#endif
if (platform_process_is_elevated())
{
2018-04-24 14:18:05 +02:00
std::string elevationWarning = _localisationService->GetString(STR_ADMIN_NOT_RECOMMENDED);
if (gOpenRCT2Headless)
{
Console::Error::WriteLine(elevationWarning.c_str());
}
else
{
_uiContext->ShowMessageBox(elevationWarning);
}
}
if (Platform::IsRunningInWine())
{
std::string wineWarning = _localisationService->GetString(STR_WINE_NOT_RECOMMENDED);
if (gOpenRCT2Headless)
{
Console::Error::WriteLine(wineWarning.c_str());
}
else
{
_uiContext->ShowMessageBox(wineWarning);
}
}
if (!gOpenRCT2Headless)
{
2017-06-26 00:45:19 +02:00
_uiContext->CreateWindow();
}
EnsureUserContentDirectoriesExist();
// TODO Ideally we want to delay this until we show the title so that we can
// still open the game window and draw a progress screen for the creation
// of the object cache.
2018-04-27 00:48:25 +02:00
_objectRepository->LoadOrConstruct(_localisationService->GetCurrentLanguage());
// TODO Like objects, this can take a while if there are a lot of track designs
// its also really something really we might want to do in the background
// as its not required until the player wants to place a new ride.
2018-04-27 00:48:25 +02:00
_trackDesignRepository->Scan(_localisationService->GetCurrentLanguage());
2018-04-27 00:48:25 +02:00
_scenarioRepository->Scan(_localisationService->GetCurrentLanguage());
TitleSequenceManager::Scan();
if (!gOpenRCT2Headless)
{
Init();
PopulateDevices();
InitRideSoundsAndInfo();
gGameSoundsOff = !gConfigSound.master_sound_enabled;
}
chat_init();
CopyOriginalUserFilesOver();
if (!gOpenRCT2NoGraphics)
{
if (!LoadBaseGraphics())
{
return false;
}
#ifdef __ENABLE_LIGHTFX__
lightfx_init();
#endif
}
input_reset_place_obj_modifier();
viewport_init_all();
2017-07-23 00:42:14 +02:00
_gameState = std::make_unique<GameState>();
_gameState->InitAll(150);
_titleScreen = std::make_unique<TitleScreen>(*_gameState);
2018-03-24 20:30:10 +01:00
_uiContext->Initialise();
return true;
}
void InitialiseDrawingEngine() final override
{
assert(_drawingEngine == nullptr);
_drawingEngineType = gConfigGeneral.drawing_engine;
auto drawingEngineFactory = _uiContext->GetDrawingEngineFactory();
auto drawingEngine = drawingEngineFactory->Create(_drawingEngineType, _uiContext);
if (drawingEngine == nullptr)
{
if (_drawingEngineType == DrawingEngine::Software)
{
_drawingEngineType = DrawingEngine::None;
log_fatal("Unable to create a drawing engine.");
exit(-1);
}
else
{
log_error("Unable to create drawing engine. Falling back to software.");
// Fallback to software
gConfigGeneral.drawing_engine = DrawingEngine::Software;
config_save_default();
drawing_engine_init();
}
}
else
{
try
{
drawingEngine->Initialise();
drawingEngine->SetVSync(gConfigGeneral.use_vsync);
_drawingEngine = std::move(drawingEngine);
}
2018-06-22 23:25:16 +02:00
catch (const std::exception& ex)
{
if (_drawingEngineType == DrawingEngine::Software)
{
_drawingEngineType = DrawingEngine::None;
log_error(ex.what());
log_fatal("Unable to initialise a drawing engine.");
exit(-1);
}
else
{
log_error(ex.what());
log_error("Unable to initialise drawing engine. Falling back to software.");
// Fallback to software
gConfigGeneral.drawing_engine = DrawingEngine::Software;
config_save_default();
drawing_engine_init();
}
}
}
window_check_all_valid_zoom();
}
void DisposeDrawingEngine() final override
{
_drawingEngine = nullptr;
}
bool LoadParkFromFile(
const std::string& path, bool loadTitleScreenOnFail = false, bool asScenario = false) final override
2017-07-02 20:10:22 +02:00
{
2019-07-22 23:12:48 +02:00
log_verbose("Context::LoadParkFromFile(%s)", path.c_str());
try
{
2020-07-09 23:33:45 +02:00
if (String::Equals(Path::GetExtension(path), ".sea", true))
{
auto data = DecryptSea(fs::u8path(path));
auto ms = MemoryStream(data.data(), data.size(), MEMORY_ACCESS::READ);
if (!LoadParkFromStream(&ms, path, loadTitleScreenOnFail, asScenario))
{
throw std::runtime_error(".sea file may have been renamed.");
}
return true;
2020-07-09 23:33:45 +02:00
}
2021-09-15 22:22:15 +02:00
auto fs = FileStream(path, FILE_MODE_OPEN);
if (!LoadParkFromStream(&fs, path, loadTitleScreenOnFail, asScenario))
2020-07-09 23:33:45 +02:00
{
2021-09-15 22:22:15 +02:00
return false;
2020-07-09 23:33:45 +02:00
}
2021-09-15 22:22:15 +02:00
return true;
}
2018-06-22 23:25:16 +02:00
catch (const std::exception& e)
{
Console::Error::WriteLine(e.what());
if (loadTitleScreenOnFail)
{
title_load();
}
auto windowManager = _uiContext->GetWindowManager();
windowManager->ShowError(STR_FAILED_TO_LOAD_FILE_CONTAINS_INVALID_DATA, STR_NONE, {});
}
return false;
2017-07-02 20:10:22 +02:00
}
bool LoadParkFromStream(
IStream* stream, const std::string& path, bool loadTitleScreenFirstOnFail = false,
bool asScenario = false) final override
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
{
try
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
{
ClassifiedFileInfo info;
if (!TryClassifyFile(stream, &info))
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
{
throw std::runtime_error("Unable to detect file type");
}
2018-12-15 13:46:32 +01:00
if (info.Type != FILE_TYPE::PARK && info.Type != FILE_TYPE::SAVED_GAME && info.Type != FILE_TYPE::SCENARIO)
{
throw std::runtime_error("Invalid file type.");
}
std::unique_ptr<IParkImporter> parkImporter;
2018-12-15 13:46:32 +01:00
if (info.Type == FILE_TYPE::PARK)
{
parkImporter = ParkImporter::CreateParkFile(*_objectRepository);
}
else if (info.Version <= FILE_TYPE_S4_CUTOFF)
{
// Save is an S4 (RCT1 format)
parkImporter = ParkImporter::CreateS4();
}
else
{
// Save is an S6 (RCT2 format)
parkImporter = ParkImporter::CreateS6(*_objectRepository);
}
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
auto result = parkImporter->LoadFromStream(stream, info.Type == FILE_TYPE::SCENARIO, false, path.c_str());
// From this point onwards the currently loaded park will be corrupted if loading fails
// so reload the title screen if that happens.
loadTitleScreenFirstOnFail = true;
_objectManager->LoadObjects(result.RequiredObjects);
parkImporter->Import();
gScenarioSavePath = path;
gCurrentLoadedPath = path;
gFirstTimeSaving = true;
game_fix_save_vars();
AutoCreateMapAnimations();
EntityTweener::Get().Reset();
gScreenAge = 0;
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
2021-08-17 09:36:18 +02:00
#ifndef DISABLE_NETWORK
bool sendMap = false;
2021-08-17 09:36:18 +02:00
#endif
if (!asScenario && (info.Type == FILE_TYPE::PARK || info.Type == FILE_TYPE::SAVED_GAME))
{
2021-08-17 09:36:18 +02:00
#ifndef DISABLE_NETWORK
if (_network.GetMode() == NETWORK_MODE_CLIENT)
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
{
_network.Close();
}
2021-08-17 09:36:18 +02:00
#endif
game_load_init();
2021-08-17 09:36:18 +02:00
#ifndef DISABLE_NETWORK
if (_network.GetMode() == NETWORK_MODE_SERVER)
{
sendMap = true;
}
2021-08-17 09:36:18 +02:00
#endif
}
else
{
scenario_begin();
2021-08-17 09:36:18 +02:00
#ifndef DISABLE_NETWORK
if (_network.GetMode() == NETWORK_MODE_SERVER)
{
sendMap = true;
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
}
if (_network.GetMode() == NETWORK_MODE_CLIENT)
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
{
_network.Close();
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
}
2021-08-17 09:36:18 +02:00
#endif
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
}
// This ensures that the newly loaded save reflects the user's
// 'show real names of guests' option, now that it's a global setting
peep_update_names(gConfigGeneral.show_real_names_of_guests);
2021-08-17 09:36:18 +02:00
#ifndef DISABLE_NETWORK
if (sendMap)
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
{
_network.Server_Send_MAP();
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
}
2021-08-17 09:36:18 +02:00
#endif
#ifdef USE_BREAKPAD
if (_network.GetMode() == NETWORK_MODE_NONE)
{
start_silent_record();
}
#endif
return true;
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
}
catch (const ObjectLoadException& e)
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
{
// If loading the SV6 or SV4 failed return to the title screen if requested.
if (loadTitleScreenFirstOnFail)
{
title_load();
}
// The path needs to be duplicated as it's a const here
// which the window function doesn't like
auto intent = Intent(WC_OBJECT_LOAD_ERROR);
intent.putExtra(INTENT_EXTRA_PATH, path);
intent.putExtra(INTENT_EXTRA_LIST, const_cast<ObjectEntryDescriptor*>(e.MissingObjects.data()));
intent.putExtra(INTENT_EXTRA_LIST_COUNT, static_cast<uint32_t>(e.MissingObjects.size()));
auto windowManager = _uiContext->GetWindowManager();
windowManager->OpenIntent(&intent);
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
}
catch (const UnsupportedRCTCFlagException& e)
{
// If loading the SV6 or SV4 failed return to the title screen if requested.
if (loadTitleScreenFirstOnFail)
{
title_load();
}
auto windowManager = _uiContext->GetWindowManager();
auto ft = Formatter();
ft.Add<uint16_t>(e.Flag);
windowManager->ShowError(STR_FAILED_TO_LOAD_IMCOMPATIBLE_RCTC_FLAG, STR_NONE, ft);
}
catch (const UnsupportedRideTypeException&)
{
// If loading the SV6 or SV4 failed return to the title screen if requested.
if (loadTitleScreenFirstOnFail)
{
title_load();
}
auto windowManager = _uiContext->GetWindowManager();
windowManager->ShowError(STR_FILE_CONTAINS_UNSUPPORTED_RIDE_TYPES, STR_NONE, {});
}
catch (const std::exception& e)
{
// If loading the SV6 or SV4 failed return to the title screen if requested.
if (loadTitleScreenFirstOnFail)
{
title_load();
}
Console::Error::WriteLine(e.what());
}
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
return false;
}
private:
std::string GetOrPromptRCT2Path()
{
auto result = std::string();
if (String::IsNullOrEmpty(gCustomRCT2DataPath))
{
// Check install directory
if (gConfigGeneral.rct2_path == nullptr || !platform_original_game_data_exists(gConfigGeneral.rct2_path))
{
log_verbose("install directory does not exist or invalid directory selected, %s", gConfigGeneral.rct2_path);
if (!config_find_or_browse_install_directory())
{
utf8 path[MAX_PATH];
config_get_default_path(path, sizeof(path));
2018-06-22 23:25:16 +02:00
Console::Error::WriteLine(
"An RCT2 install directory must be specified! Please edit \"game_path\" in %s.\n", path);
return std::string();
}
}
result = std::string(gConfigGeneral.rct2_path);
}
else
{
result = std::string(gCustomRCT2DataPath);
}
return result;
}
bool LoadBaseGraphics()
{
if (!gfx_load_g1(*_env))
{
return false;
}
2018-02-05 19:26:27 +01:00
gfx_load_g2();
gfx_load_csg();
font_sprite_initialise_characters();
return true;
}
/**
* Launches the game, after command line arguments have been parsed and processed.
*/
void Launch()
{
if (!_versionCheckFuture.valid())
{
_versionCheckFuture = std::async(std::launch::async, [this] {
_newVersionInfo = get_latest_version();
if (!String::StartsWith(gVersionInfoTag, _newVersionInfo.tag))
{
_hasNewVersionInfo = true;
}
});
}
gIntroState = IntroState::None;
if (gOpenRCT2Headless)
{
// NONE or OPEN are the only allowed actions for headless mode
if (gOpenRCT2StartupAction != StartupAction::Open)
{
gOpenRCT2StartupAction = StartupAction::None;
}
}
else
{
if ((gOpenRCT2StartupAction == StartupAction::Title) && gConfigGeneral.play_intro)
{
gOpenRCT2StartupAction = StartupAction::Intro;
}
}
2018-06-22 23:25:16 +02:00
switch (gOpenRCT2StartupAction)
{
case StartupAction::Intro:
gIntroState = IntroState::PublisherBegin;
2018-06-22 23:25:16 +02:00
title_load();
break;
case StartupAction::Title:
2018-06-22 23:25:16 +02:00
title_load();
break;
case StartupAction::Open:
{
2018-06-22 23:25:16 +02:00
// A path that includes "://" is illegal with all common filesystems, so it is almost certainly a URL
// This way all cURL supported protocols, like http, ftp, scp and smb are automatically handled
if (strstr(gOpenRCT2StartupActionPath, "://") != nullptr)
{
2018-06-22 23:25:16 +02:00
#ifndef DISABLE_HTTP
// Download park and open it using its temporary filename
auto data = DownloadPark(gOpenRCT2StartupActionPath);
if (data.empty())
2018-06-22 23:25:16 +02:00
{
title_load();
break;
}
auto ms = MemoryStream(data.data(), data.size(), MEMORY_ACCESS::READ);
2018-06-22 23:25:16 +02:00
if (!LoadParkFromStream(&ms, gOpenRCT2StartupActionPath, true))
{
2018-06-22 23:25:16 +02:00
Console::Error::WriteLine("Failed to load '%s'", gOpenRCT2StartupActionPath);
title_load();
break;
}
2018-06-22 23:25:16 +02:00
#endif
2017-07-02 20:10:22 +02:00
}
2018-06-22 23:25:16 +02:00
else
2017-07-02 20:10:22 +02:00
{
2018-06-22 23:25:16 +02:00
try
{
if (!LoadParkFromFile(gOpenRCT2StartupActionPath, true))
{
break;
}
}
catch (const std::exception& ex)
{
Console::Error::WriteLine("Failed to load '%s'", gOpenRCT2StartupActionPath);
Console::Error::WriteLine("%s", ex.what());
title_load();
break;
}
2017-07-02 20:10:22 +02:00
}
2018-06-22 23:25:16 +02:00
gScreenFlags = SCREEN_FLAGS_PLAYING;
#ifndef DISABLE_NETWORK
2018-06-22 23:25:16 +02:00
if (gNetworkStart == NETWORK_MODE_SERVER)
{
2018-06-22 23:25:16 +02:00
if (gNetworkStartPort == 0)
{
gNetworkStartPort = gConfigNetwork.default_port;
}
2019-02-23 15:54:17 +01:00
if (gNetworkStartAddress.empty())
2018-06-22 23:25:16 +02:00
{
gNetworkStartAddress = gConfigNetwork.listen_address;
}
2018-06-22 23:25:16 +02:00
if (String::IsNullOrEmpty(gCustomPassword))
{
_network.SetPassword(gConfigNetwork.default_password.c_str());
2018-06-22 23:25:16 +02:00
}
else
{
_network.SetPassword(gCustomPassword);
2018-06-22 23:25:16 +02:00
}
_network.BeginServer(gNetworkStartPort, gNetworkStartAddress);
2018-06-22 23:25:16 +02:00
}
else
2018-06-22 23:25:16 +02:00
#endif // DISABLE_NETWORK
{
game_load_scripts();
}
2018-06-22 23:25:16 +02:00
break;
}
case StartupAction::Edit:
2018-06-22 23:25:16 +02:00
if (String::SizeOf(gOpenRCT2StartupActionPath) == 0)
{
2018-06-22 23:25:16 +02:00
Editor::Load();
}
2018-06-22 23:25:16 +02:00
else if (!Editor::LoadLandscape(gOpenRCT2StartupActionPath))
{
2018-06-22 23:25:16 +02:00
title_load();
}
2018-06-22 23:25:16 +02:00
break;
default:
break;
}
#ifndef DISABLE_NETWORK
if (gNetworkStart == NETWORK_MODE_CLIENT)
{
if (gNetworkStartPort == 0)
{
gNetworkStartPort = gConfigNetwork.default_port;
}
_network.BeginClient(gNetworkStartHost, gNetworkStartPort);
}
#endif // DISABLE_NETWORK
2018-03-18 01:31:02 +01:00
_stdInOutConsole.Start();
RunGameLoop();
}
bool ShouldRunVariableFrame()
{
2018-06-22 23:25:16 +02:00
if (!gConfigGeneral.uncap_fps)
return false;
if (gGameSpeed > 4)
return false;
if (gOpenRCT2Headless)
return false;
if (_uiContext->IsMinimised())
return false;
return true;
}
/**
2018-06-22 23:25:16 +02:00
* Run the main game loop until the finished flag is set.
*/
void RunGameLoop()
{
log_verbose("begin openrct2 loop");
_finished = false;
2017-09-04 23:17:35 +02:00
#ifndef __EMSCRIPTEN__
_variableFrame = ShouldRunVariableFrame();
do
{
2017-09-07 13:31:28 +02:00
RunFrame();
2018-06-22 23:25:16 +02:00
} while (!_finished);
2017-09-04 23:17:35 +02:00
#else
2018-06-22 23:25:16 +02:00
emscripten_set_main_loop_arg(
[](void* vctx) -> {
auto ctx = reinterpret_cast<Context*>(vctx);
ctx->RunFrame();
2018-06-22 23:25:16 +02:00
},
this, 0, 1);
2017-09-04 23:17:35 +02:00
#endif // __EMSCRIPTEN__
log_verbose("finish openrct2 loop");
}
void RunFrame()
{
// Make sure we catch the state change and reset it.
bool useVariableFrame = ShouldRunVariableFrame();
if (_variableFrame != useVariableFrame)
{
_lastTick = 0;
_variableFrame = useVariableFrame;
// Switching from variable to fixed frame requires reseting
// of entity positions back to end of tick positions
auto& tweener = EntityTweener::Get();
tweener.Restore();
tweener.Reset();
}
if (useVariableFrame)
{
RunVariableFrame();
}
else
{
RunFixedFrame();
}
}
void RunFixedFrame()
{
uint32_t currentTick = platform_get_ticks();
if (_lastTick == 0)
{
_lastTick = currentTick;
}
float elapsed = (currentTick - _lastTick) * _timeScale;
_lastTick = currentTick;
_accumulator = std::min(_accumulator + elapsed, static_cast<float>(GAME_UPDATE_MAX_THRESHOLD));
2017-06-26 00:45:19 +02:00
_uiContext->ProcessMessages();
if (_accumulator < GAME_UPDATE_TIME_MS)
{
platform_sleep(GAME_UPDATE_TIME_MS - _accumulator - 1);
return;
}
while (_accumulator >= GAME_UPDATE_TIME_MS)
{
Update();
// Always run this at a fixed rate, Update can cause multiple ticks if the game is speed up.
window_update_all();
_accumulator -= GAME_UPDATE_TIME_MS;
}
if (!_isWindowMinimised && !gOpenRCT2Headless)
{
_drawingEngine->BeginDraw();
_painter->Paint(*_drawingEngine);
_drawingEngine->EndDraw();
}
}
void RunVariableFrame()
{
uint32_t currentTick = platform_get_ticks();
auto& tweener = EntityTweener::Get();
bool draw = !_isWindowMinimised && !gOpenRCT2Headless;
if (_lastTick == 0)
{
tweener.Reset();
_lastTick = currentTick;
}
float elapsed = (currentTick - _lastTick) * _timeScale;
_lastTick = currentTick;
_accumulator = std::min(_accumulator + elapsed, static_cast<float>(GAME_UPDATE_MAX_THRESHOLD));
2017-06-26 00:45:19 +02:00
_uiContext->ProcessMessages();
while (_accumulator >= GAME_UPDATE_TIME_MS)
{
// Get the original position of each sprite
2018-06-22 23:25:16 +02:00
if (draw)
tweener.PreTick();
2017-06-26 00:45:19 +02:00
Update();
// Always run this at a fixed rate, Update can cause multiple ticks if the game is speed up.
window_update_all();
_accumulator -= GAME_UPDATE_TIME_MS;
// Get the next position of each sprite
2018-06-22 23:25:16 +02:00
if (draw)
tweener.PostTick();
}
if (draw)
{
const float alpha = std::min(_accumulator / static_cast<float>(GAME_UPDATE_TIME_MS), 1.0f);
tweener.Tween(alpha);
_drawingEngine->BeginDraw();
_painter->Paint(*_drawingEngine);
_drawingEngine->EndDraw();
}
}
2017-06-26 00:45:19 +02:00
void Update()
{
uint32_t currentUpdateTime = platform_get_ticks();
gCurrentDeltaTime = std::min<uint32_t>(currentUpdateTime - _lastUpdateTime, 500);
_lastUpdateTime = currentUpdateTime;
2017-06-26 00:45:19 +02:00
if (game_is_not_paused())
{
gPaletteEffectFrame += gCurrentDeltaTime;
2017-06-26 00:45:19 +02:00
}
date_update_real_time_of_day();
if (gIntroState != IntroState::None)
2017-06-26 00:45:19 +02:00
{
intro_update();
}
else if ((gScreenFlags & SCREEN_FLAGS_TITLE_DEMO) && !gOpenRCT2Headless)
{
2017-07-23 00:42:14 +02:00
_titleScreen->Update();
2017-06-26 00:45:19 +02:00
}
else
{
_gameState->Update();
2017-06-26 00:45:19 +02:00
}
2017-11-10 21:23:23 +01:00
#ifdef __ENABLE_DISCORD__
if (_discordService != nullptr)
{
_discordService->Update();
}
#endif
2017-06-26 00:45:19 +02:00
chat_update();
#ifdef ENABLE_SCRIPTING
_scriptEngine.Update();
2020-02-23 13:55:48 +01:00
#endif
_stdInOutConsole.ProcessEvalQueue();
2018-03-11 23:25:34 +01:00
_uiContext->Update();
2017-06-26 00:45:19 +02:00
}
/**
* Ensure that the custom user content folders are present
*/
void EnsureUserContentDirectoriesExist()
{
EnsureDirectoriesExist(
DIRBASE::USER,
{
DIRID::OBJECT,
DIRID::SAVE,
DIRID::SCENARIO,
DIRID::TRACK,
DIRID::LANDSCAPE,
DIRID::HEIGHTMAP,
2020-04-26 15:52:57 +02:00
DIRID::PLUGIN,
DIRID::THEME,
DIRID::SEQUENCE,
DIRID::REPLAY,
DIRID::LOG_DESYNCS,
DIRID::CRASH,
});
}
void EnsureDirectoriesExist(const DIRBASE dirBase, const std::initializer_list<DIRID>& dirIds)
{
for (const auto& dirId : dirIds)
{
auto path = _env->GetDirectoryPath(dirBase, dirId);
if (!platform_ensure_directory_exists(path.c_str()))
log_error("Unable to create directory '%s'.", path.c_str());
}
}
/**
2018-06-22 23:25:16 +02:00
* Copy saved games and landscapes to user directory
*/
void CopyOriginalUserFilesOver()
{
CopyOriginalUserFilesOver(DIRID::SAVE, "*.sv6");
CopyOriginalUserFilesOver(DIRID::LANDSCAPE, "*.sc6");
}
2018-06-22 23:25:16 +02:00
void CopyOriginalUserFilesOver(DIRID dirid, const std::string& pattern)
{
auto src = _env->GetDirectoryPath(DIRBASE::RCT2, dirid);
auto dst = _env->GetDirectoryPath(DIRBASE::USER, dirid);
CopyOriginalUserFilesOver(src, dst, pattern);
}
2018-06-22 23:25:16 +02:00
void CopyOriginalUserFilesOver(const std::string& srcRoot, const std::string& dstRoot, const std::string& pattern)
{
log_verbose("CopyOriginalUserFilesOver('%s', '%s', '%s')", srcRoot.c_str(), dstRoot.c_str(), pattern.c_str());
auto scanPattern = Path::Combine(srcRoot, pattern);
auto scanner = Path::ScanDirectory(scanPattern, true);
while (scanner->Next())
{
auto src = std::string(scanner->GetPath());
auto dst = Path::Combine(dstRoot, scanner->GetPathRelative());
auto dstDirectory = Path::GetDirectory(dst);
// Create the directory if necessary
if (!platform_directory_exists(dstDirectory.c_str()))
{
Console::WriteLine("Creating directory '%s'", dstDirectory.c_str());
if (!platform_ensure_directory_exists(dstDirectory.c_str()))
{
Console::Error::WriteLine("Could not create directory %s.", dstDirectory.c_str());
break;
}
}
// Only copy the file if it doesn't already exist
if (!File::Exists(dst))
{
Console::WriteLine("Copying '%s' to '%s'", src.c_str(), dst.c_str());
if (!File::Copy(src, dst, false))
{
Console::Error::WriteLine("Failed to copy '%s' to '%s'", src.c_str(), dst.c_str());
}
}
}
}
#ifndef DISABLE_HTTP
std::vector<uint8_t> DownloadPark(const std::string& url)
{
// Download park to buffer in memory
Http::Request request;
request.url = url;
request.method = Http::Method::GET;
Http::Response res;
try
{
res = Do(request);
if (res.status != Http::Status::Ok)
throw std::runtime_error("bad http status");
}
catch (std::exception& e)
{
Console::Error::WriteLine("Failed to download '%s', cause %s", request.url.c_str(), e.what());
return {};
}
std::vector<uint8_t> parkData;
parkData.resize(res.body.size());
std::memcpy(parkData.data(), res.body.c_str(), parkData.size());
return parkData;
}
#endif
bool HasNewVersionInfo() const override
{
return _hasNewVersionInfo;
}
const NewVersionInfo* GetNewVersionInfo() const override
{
return &_newVersionInfo;
}
void SetTimeScale(float newScale) override
{
_timeScale = std::clamp(newScale, GAME_MIN_TIME_SCALE, GAME_MAX_TIME_SCALE);
}
float GetTimeScale() const override
{
return _timeScale;
}
};
2018-06-22 23:25:16 +02:00
Context* Context::Instance = nullptr;
std::unique_ptr<IContext> CreateContext()
{
return CreateContext(CreatePlatformEnvironment(), CreateDummyAudioContext(), CreateDummyUiContext());
}
std::unique_ptr<IContext> CreateContext(
const std::shared_ptr<IPlatformEnvironment>& env, const std::shared_ptr<Audio::IAudioContext>& audioContext,
2018-04-27 19:47:57 +02:00
const std::shared_ptr<IUiContext>& uiContext)
{
return std::make_unique<Context>(env, audioContext, uiContext);
}
2018-06-22 23:25:16 +02:00
IContext* GetContext()
{
return Context::Instance;
}
2018-06-22 23:25:16 +02:00
} // namespace OpenRCT2
2018-02-01 18:49:14 +01:00
void context_init()
{
2018-02-01 18:49:14 +01:00
GetContext()->GetUiContext()->GetWindowManager()->Init();
}
2017-09-10 11:02:16 +02:00
2018-06-22 23:25:16 +02:00
bool context_load_park_from_file(const utf8* path)
2018-02-01 18:49:14 +01:00
{
return GetContext()->LoadParkFromFile(path);
}
2018-06-22 23:25:16 +02:00
bool context_load_park_from_stream(void* stream)
2018-02-01 18:49:14 +01:00
{
return GetContext()->LoadParkFromStream(static_cast<IStream*>(stream), "");
2018-02-01 18:49:14 +01:00
}
Feature: Preview title sequences in-game Title sequences can now be played back in-game, allowing for much easier editing. Improved title sequence playback in general. Clicking play while on a different title sequence will play the new one. Clicking stop will make the title screen go back to the config title sequence. And the closing the title sequence window will also make the game go back to the config title sequence, and reload the sequence if it was modified. Changes made to title sequences in-game are now correctly loaded in the title screen. Starting a title sequence within the editor will now always reset it even if it's the current playing sequence. (Not for playing in the editor though). Get Location in title sequence command editor now has 100% accuracy compared to before where it would usually get some offset value. Added `get_map_coordinates_from_pos_window` which will allow getting the viewport coordinates of a specific window even if the input coordinates are under another window. This has use with getting 2D positions from the main window without the other windows getting in the way. Options window will now always specify the config title sequence in the dropdown and not the current title sequence. Made a global variable `gLoadKeepWindowsOpen`, in game.h to keep windows open when loading a park. When loading a title sequence park in-game. The sequence player will force-close all park-specific windows ahead of time. Skipping while testing title sequences no longer needs to reload the park if the current playback position is already before the target position and ahead of the load position. Added changelog entry.
2017-10-30 12:07:01 +01:00
2018-06-22 23:25:16 +02:00
void openrct2_write_full_version_info(utf8* buffer, size_t bufferSize)
2018-02-01 18:49:14 +01:00
{
String::Set(buffer, bufferSize, gVersionInfoFull);
}
2018-02-01 18:49:14 +01:00
void openrct2_finish()
{
GetContext()->Finish();
}
void context_setcurrentcursor(CursorID cursor)
2018-02-01 18:49:14 +01:00
{
GetContext()->GetUiContext()->SetCursor(cursor);
2018-02-01 18:49:14 +01:00
}
2017-03-25 18:42:14 +01:00
2018-02-01 18:49:14 +01:00
void context_update_cursor_scale()
{
GetContext()->GetUiContext()->SetCursorScale(static_cast<uint8_t>(std::round(gConfigGeneral.window_scale)));
2018-02-01 18:49:14 +01:00
}
2017-12-09 17:08:38 +01:00
2018-02-01 18:49:14 +01:00
void context_hide_cursor()
{
GetContext()->GetUiContext()->SetCursorVisible(false);
}
2017-03-25 18:42:14 +01:00
2018-02-01 18:49:14 +01:00
void context_show_cursor()
{
GetContext()->GetUiContext()->SetCursorVisible(true);
}
2017-03-25 18:42:14 +01:00
ScreenCoordsXY context_get_cursor_position()
2018-02-01 18:49:14 +01:00
{
return GetContext()->GetUiContext()->GetCursorPosition();
2018-02-01 18:49:14 +01:00
}
2017-03-25 18:42:14 +01:00
ScreenCoordsXY context_get_cursor_position_scaled()
2018-02-01 18:49:14 +01:00
{
auto cursorCoords = context_get_cursor_position();
2018-02-01 18:49:14 +01:00
// Compensate for window scaling.
return { static_cast<int32_t>(std::ceil(cursorCoords.x / gConfigGeneral.window_scale)),
static_cast<int32_t>(std::ceil(cursorCoords.y / gConfigGeneral.window_scale)) };
2018-02-01 18:49:14 +01:00
}
2017-03-25 18:42:14 +01:00
void context_set_cursor_position(const ScreenCoordsXY& cursorPosition)
2018-02-01 18:49:14 +01:00
{
GetContext()->GetUiContext()->SetCursorPosition(cursorPosition);
2018-02-01 18:49:14 +01:00
}
2017-03-25 18:42:14 +01:00
2018-06-22 23:25:16 +02:00
const CursorState* context_get_cursor_state()
2018-02-01 18:49:14 +01:00
{
return GetContext()->GetUiContext()->GetCursorState();
}
2017-03-25 18:42:14 +01:00
2018-06-22 23:25:16 +02:00
const uint8_t* context_get_keys_state()
2018-02-01 18:49:14 +01:00
{
return GetContext()->GetUiContext()->GetKeysState();
}
2017-03-25 18:42:14 +01:00
2018-06-22 23:25:16 +02:00
const uint8_t* context_get_keys_pressed()
2018-02-01 18:49:14 +01:00
{
return GetContext()->GetUiContext()->GetKeysPressed();
}
2017-03-25 18:42:14 +01:00
2018-06-22 23:25:16 +02:00
TextInputSession* context_start_text_input(utf8* buffer, size_t maxLength)
2018-02-01 18:49:14 +01:00
{
return GetContext()->GetUiContext()->StartTextInput(buffer, maxLength);
}
2017-03-25 18:42:14 +01:00
2018-02-01 18:49:14 +01:00
void context_stop_text_input()
{
GetContext()->GetUiContext()->StopTextInput();
}
2017-03-25 18:42:14 +01:00
2018-02-01 18:49:14 +01:00
bool context_is_input_active()
{
return GetContext()->GetUiContext()->IsTextInputActive();
}
2017-03-26 04:36:22 +02:00
2018-02-01 18:49:14 +01:00
void context_trigger_resize()
{
return GetContext()->GetUiContext()->TriggerResize();
}
2017-03-26 04:36:22 +02:00
void context_set_fullscreen_mode(int32_t mode)
2018-02-01 18:49:14 +01:00
{
return GetContext()->GetUiContext()->SetFullscreenMode(static_cast<FULLSCREEN_MODE>(mode));
2018-02-01 18:49:14 +01:00
}
2017-03-26 04:36:22 +02:00
2018-02-01 18:49:14 +01:00
void context_recreate_window()
{
GetContext()->GetUiContext()->RecreateWindow();
}
int32_t context_get_width()
2018-02-01 18:49:14 +01:00
{
return GetContext()->GetUiContext()->GetWidth();
}
int32_t context_get_height()
2018-02-01 18:49:14 +01:00
{
return GetContext()->GetUiContext()->GetHeight();
}
2017-04-01 14:38:52 +02:00
2018-02-01 18:49:14 +01:00
bool context_has_focus()
{
return GetContext()->GetUiContext()->HasFocus();
}
2017-04-01 14:38:52 +02:00
2018-02-01 18:49:14 +01:00
void context_set_cursor_trap(bool value)
{
GetContext()->GetUiContext()->SetCursorTrap(value);
}
2018-06-22 23:25:16 +02:00
rct_window* context_open_window(rct_windowclass wc)
2018-02-01 18:49:14 +01:00
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
return windowManager->OpenWindow(wc);
}
2018-06-22 23:25:16 +02:00
rct_window* context_open_window_view(rct_windowclass wc)
2018-02-01 18:49:14 +01:00
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
return windowManager->OpenView(wc);
}
2017-09-05 20:55:18 +02:00
2018-06-22 23:25:16 +02:00
rct_window* context_open_detail_window(uint8_t type, int32_t id)
2018-02-01 18:49:14 +01:00
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
return windowManager->OpenDetails(type, id);
}
2017-09-06 15:09:18 +02:00
2018-06-22 23:25:16 +02:00
rct_window* context_open_intent(Intent* intent)
2018-02-01 18:49:14 +01:00
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
return windowManager->OpenIntent(intent);
}
2017-09-10 11:02:16 +02:00
2018-06-22 23:25:16 +02:00
void context_broadcast_intent(Intent* intent)
2018-02-01 18:49:14 +01:00
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
windowManager->BroadcastIntent(*intent);
}
2017-10-06 23:03:48 +02:00
2018-02-01 18:49:14 +01:00
void context_force_close_window_by_class(rct_windowclass windowClass)
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
windowManager->ForceClose(windowClass);
}
rct_window* context_show_error(rct_string_id title, rct_string_id message, const Formatter& args)
2018-02-01 18:49:14 +01:00
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
return windowManager->ShowError(title, message, args);
2018-02-01 18:49:14 +01:00
}
2018-02-01 18:49:14 +01:00
void context_update_map_tooltip()
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
windowManager->UpdateMapTooltip();
}
2017-10-21 17:57:37 +02:00
2018-02-01 18:49:14 +01:00
void context_handle_input()
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
windowManager->HandleInput();
}
2017-12-07 22:16:20 +01:00
2018-02-01 18:49:14 +01:00
void context_input_handle_keyboard(bool isTitle)
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
windowManager->HandleKeyboard(isTitle);
}
2018-02-01 18:49:14 +01:00
void context_quit()
{
GetContext()->Quit();
}
2017-07-28 19:43:48 +02:00
2018-06-22 23:25:16 +02:00
const utf8* context_get_path_legacy(int32_t pathId)
2018-02-01 18:49:14 +01:00
{
static utf8 result[MAX_PATH];
auto path = GetContext()->GetPathLegacy(pathId);
String::Set(result, sizeof(result), path.c_str());
return result;
}
2018-06-22 23:25:16 +02:00
bool platform_open_common_file_dialog(utf8* outFilename, file_dialog_desc* desc, size_t outSize)
2018-02-01 18:49:14 +01:00
{
try
{
2018-02-01 18:49:14 +01:00
FileDialogDesc desc2;
desc2.Type = static_cast<FILE_DIALOG_TYPE>(desc->type);
2018-02-01 18:49:14 +01:00
desc2.Title = String::ToStd(desc->title);
desc2.InitialDirectory = String::ToStd(desc->initial_directory);
desc2.DefaultFilename = String::ToStd(desc->default_filename);
2018-06-22 23:25:16 +02:00
for (const auto& filter : desc->filters)
{
2018-02-01 18:49:14 +01:00
if (filter.name != nullptr)
{
2018-02-01 18:49:14 +01:00
desc2.Filters.push_back({ String::ToStd(filter.name), String::ToStd(filter.pattern) });
}
}
2018-02-01 18:49:14 +01:00
std::string result = GetContext()->GetUiContext()->ShowFileDialog(desc2);
String::Set(outFilename, outSize, result.c_str());
return !result.empty();
}
2018-06-22 23:25:16 +02:00
catch (const std::exception& ex)
{
2018-02-01 18:49:14 +01:00
log_error(ex.what());
outFilename[0] = '\0';
return false;
}
2018-02-01 18:49:14 +01:00
}
2018-06-22 23:25:16 +02:00
utf8* platform_open_directory_browser(const utf8* title)
2018-02-01 18:49:14 +01:00
{
try
{
2018-02-01 18:49:14 +01:00
std::string result = GetContext()->GetUiContext()->ShowDirectoryDialog(title);
return String::Duplicate(result.c_str());
}
2018-06-22 23:25:16 +02:00
catch (const std::exception& ex)
{
2018-02-01 18:49:14 +01:00
log_error(ex.what());
return nullptr;
}
2018-02-01 18:49:14 +01:00
}
/**
* This function is deprecated.
* Use IPlatformEnvironment instead.
2018-02-01 18:49:14 +01:00
*/
2018-06-22 23:25:16 +02:00
void platform_get_user_directory(utf8* outPath, const utf8* subDirectory, size_t outSize)
2018-02-01 18:49:14 +01:00
{
auto env = GetContext()->GetPlatformEnvironment();
auto path = env->GetDirectoryPath(DIRBASE::USER);
if (!String::IsNullOrEmpty(subDirectory))
2017-12-02 00:48:46 +01:00
{
2018-02-01 18:49:14 +01:00
path = Path::Combine(path, subDirectory);
2017-12-02 00:48:46 +01:00
}
String::Set(outPath, outSize, path.c_str());
}
2018-02-01 18:49:14 +01:00
/**
* This function is deprecated.
* Use IPlatformEnvironment instead.
2018-02-01 18:49:14 +01:00
*/
void platform_get_openrct2_data_path(utf8* outPath, size_t outSize)
2018-02-01 18:49:14 +01:00
{
auto env = GetContext()->GetPlatformEnvironment();
auto path = env->GetDirectoryPath(DIRBASE::OPENRCT2);
String::Set(outPath, outSize, path.c_str());
}