OpenRCT2/src/openrct2/Context.cpp

1454 lines
48 KiB
C++
Raw Normal View History

/*****************************************************************************
* Copyright (c) 2014-2019 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.hpp"
#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"
#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/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"
2018-06-22 23:25:16 +02:00
#include "world/Park.h"
2018-11-21 23:16:04 +01:00
#include <algorithm>
#include <cmath>
#include <exception>
#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
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
int32_t _drawingEngineType = DRAWING_ENGINE_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;
uint32_t _accumulator = 0;
uint32_t _lastUpdateTime = 0;
2018-06-22 23:25:16 +02:00
bool _variableFrame = false;
// If set, will end the OpenRCT2 game loop. Intentially private to this module so that the flag can not be set back to
// false.
bool _finished = 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)
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();
network_close();
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();
gfx_unload_g2();
gfx_unload_g1();
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();
}
int32_t GetDrawingEngineType() override
{
return _drawingEngineType;
}
2018-06-22 23:25:16 +02:00
IDrawingEngine* GetDrawingEngine() override
{
return _drawingEngine.get();
}
2019-02-19 16:03:08 +01:00
virtual Paint::Painter* GetPainter() override
{
return _painter.get();
}
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);
}
/**
* 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 = PM_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();
}
// 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);
}
_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
2018-04-24 14:18:05 +02:00
try
{
2018-04-24 14:18:05 +02:00
_localisationService->OpenLanguage(gConfigGeneral.language, *_objectManager);
}
catch (const std::exception& e)
{
log_error("Failed to open configured language: %s", e.what());
try
{
_localisationService->OpenLanguage(LANGUAGE_ENGLISH_UK, *_objectManager);
}
catch (const std::exception&)
{
2018-04-24 14:18:05 +02:00
log_fatal("Failed to open fallback language: %s", e.what());
return false;
}
}
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)
{
audio_init();
audio_populate_devices();
audio_init_ride_sounds_and_info();
gGameSoundsOff = !gConfigSound.master_sound_enabled;
}
2017-06-06 18:55:13 +02:00
network_set_env(_env);
chat_init();
CopyOriginalUserFilesOver();
if (!gOpenRCT2NoGraphics)
{
if (!LoadBaseGraphics())
{
return false;
}
#ifdef __ENABLE_LIGHTFX__
lightfx_init();
#endif
}
gScenarioTicks = 0;
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(static_cast<DRAWING_ENGINE_TYPE>(_drawingEngineType), _uiContext);
if (drawingEngine == nullptr)
{
if (_drawingEngineType == DRAWING_ENGINE_SOFTWARE)
{
_drawingEngineType = DRAWING_ENGINE_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 = DRAWING_ENGINE_SOFTWARE;
config_save_default();
drawing_engine_init();
}
}
else
{
try
{
drawingEngine->Initialise();
drawingEngine->SetVSync(gConfigGeneral.use_vsync);
_drawingEngine = std::unique_ptr<IDrawingEngine>(std::move(drawingEngine));
}
2018-06-22 23:25:16 +02:00
catch (const std::exception& ex)
{
if (_drawingEngineType == DRAWING_ENGINE_SOFTWARE)
{
_drawingEngineType = DRAWING_ENGINE_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 = DRAWING_ENGINE_SOFTWARE;
config_save_default();
drawing_engine_init();
}
}
}
}
void DisposeDrawingEngine() final override
{
_drawingEngine = nullptr;
}
2018-06-22 23:25:16 +02:00
bool LoadParkFromFile(const std::string& path, bool loadTitleScreenOnFail) 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);
return LoadParkFromStream(&ms, path, loadTitleScreenOnFail);
}
else
{
auto fs = FileStream(path, FILE_MODE_OPEN);
return LoadParkFromStream(&fs, path, loadTitleScreenOnFail);
}
}
2018-06-22 23:25:16 +02:00
catch (const std::exception& e)
{
Console::Error::WriteLine(e.what());
}
return false;
2017-07-02 20:10:22 +02:00
}
2018-06-22 23:25:16 +02:00
bool LoadParkFromStream(IStream* stream, const std::string& path, bool loadTitleScreenFirstOnFail) 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
{
ClassifiedFileInfo info;
if (TryClassifyFile(stream, &info))
{
2018-06-22 23:25:16 +02:00
if (info.Type == FILE_TYPE::SAVED_GAME || info.Type == FILE_TYPE::SCENARIO)
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
{
std::unique_ptr<IParkImporter> parkImporter;
if (info.Version <= FILE_TYPE_S4_CUTOFF)
{
// Save is an S4 (RCT1 format)
parkImporter = ParkImporter::CreateS4();
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
}
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
}
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
{
auto result = parkImporter->LoadFromStream(
stream, info.Type == FILE_TYPE::SCENARIO, false, path.c_str());
2018-05-21 20:06:21 +02:00
_objectManager->LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size());
parkImporter->Import();
gScenarioSavePath = path;
gCurrentLoadedPath = path;
gFirstTimeSaving = true;
game_fix_save_vars();
AutoCreateMapAnimations();
sprite_position_tween_reset();
gScreenAge = 0;
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;
2019-05-19 17:44:43 +02:00
bool sendMap = false;
if (info.Type == FILE_TYPE::SAVED_GAME)
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_get_mode() == 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
}
game_load_init();
if (network_get_mode() == NETWORK_MODE_SERVER)
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
{
2019-05-19 17:44:43 +02:00
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
}
}
else
{
scenario_begin();
if (network_get_mode() == NETWORK_MODE_SERVER)
{
2019-05-19 17:44:43 +02:00
sendMap = true;
}
if (network_get_mode() == NETWORK_MODE_CLIENT)
{
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
}
// 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);
2019-05-19 17:44:43 +02:00
if (sendMap)
{
network_send_map();
}
2020-03-31 23:12:35 +02:00
#ifdef USE_BREAKPAD
if (network_get_mode() == NETWORK_MODE_NONE)
{
start_silent_record();
}
#endif
return true;
}
catch (const ObjectLoadException& e)
{
// This option is used when loading parks from the command line
// to ensure that the title sequence loads before the window
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<rct_object_entry*>(e.MissingObjects.data()));
intent.putExtra(INTENT_EXTRA_LIST_COUNT, static_cast<uint32_t>(e.MissingObjects.size()));
auto windowManager = _uiContext->GetWindowManager();
windowManager->OpenIntent(&intent);
}
catch (const UnsupportedRCTCFlagException& e)
{
// This option is used when loading parks from the command line
// to ensure that the title sequence loads before the window
if (loadTitleScreenFirstOnFail)
{
title_load();
}
auto windowManager = _uiContext->GetWindowManager();
Formatter::Common().Add<uint16_t>(e.Flag);
windowManager->ShowError(STR_FAILED_TO_LOAD_IMCOMPATIBLE_RCTC_FLAG, STR_NONE);
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 std::exception& 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 for a reason other than invalid objects
// the current park state will be corrupted so just go back to the title screen.
title_load();
2018-01-02 20:23:22 +01:00
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
}
}
else
{
Console::Error::WriteLine("Invalid file type.");
}
}
else
{
Console::Error::WriteLine("Unable to detect file type.");
}
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()
{
gIntroState = INTRO_STATE_NONE;
if (gOpenRCT2Headless)
{
// NONE or OPEN are the only allowed actions for headless mode
if (gOpenRCT2StartupAction != STARTUP_ACTION_OPEN)
{
gOpenRCT2StartupAction = STARTUP_ACTION_NONE;
}
}
else
{
if ((gOpenRCT2StartupAction == STARTUP_ACTION_TITLE) && gConfigGeneral.play_intro)
{
gOpenRCT2StartupAction = STARTUP_ACTION_INTRO;
}
}
2018-06-22 23:25:16 +02:00
switch (gOpenRCT2StartupAction)
{
2018-06-22 23:25:16 +02:00
case STARTUP_ACTION_INTRO:
gIntroState = INTRO_STATE_PUBLISHER_BEGIN;
title_load();
break;
case STARTUP_ACTION_TITLE:
title_load();
break;
case STARTUP_ACTION_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))
{
2019-02-23 15:54:17 +01:00
network_set_password(gConfigNetwork.default_password.c_str());
2018-06-22 23:25:16 +02:00
}
else
{
network_set_password(gCustomPassword);
}
network_begin_server(gNetworkStartPort, gNetworkStartAddress);
}
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 STARTUP_ACTION_EDIT:
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;
}
#ifndef DISABLE_NETWORK
if (gNetworkStart == NETWORK_MODE_CLIENT)
{
if (gNetworkStartPort == 0)
{
gNetworkStartPort = gConfigNetwork.default_port;
}
network_begin_client(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;
}
if (useVariableFrame)
{
RunVariableFrame();
}
else
{
RunFixedFrame();
}
}
void RunFixedFrame()
{
uint32_t currentTick = platform_get_ticks();
if (_lastTick == 0)
{
_lastTick = currentTick;
}
uint32_t elapsed = currentTick - _lastTick;
_lastTick = currentTick;
_accumulator = std::min(_accumulator + elapsed, static_cast<uint32_t>(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();
_accumulator -= GAME_UPDATE_TIME_MS;
}
if (!_isWindowMinimised && !gOpenRCT2Headless)
{
_drawingEngine->BeginDraw();
_painter->Paint(*_drawingEngine);
_drawingEngine->EndDraw();
_drawingEngine->UpdateWindows();
}
}
void RunVariableFrame()
{
uint32_t currentTick = platform_get_ticks();
bool draw = !_isWindowMinimised && !gOpenRCT2Headless;
if (_lastTick == 0)
{
sprite_position_tween_reset();
_lastTick = currentTick;
}
uint32_t elapsed = currentTick - _lastTick;
_lastTick = currentTick;
_accumulator = std::min(_accumulator + elapsed, static_cast<uint32_t>(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)
sprite_position_tween_store_a();
2017-06-26 00:45:19 +02:00
Update();
_accumulator -= GAME_UPDATE_TIME_MS;
// Get the next position of each sprite
2018-06-22 23:25:16 +02:00
if (draw)
sprite_position_tween_store_b();
}
if (draw)
{
const float alpha = std::min(static_cast<float>(_accumulator) / GAME_UPDATE_TIME_MS, 1.0f);
sprite_position_tween_all(alpha);
_drawingEngine->BeginDraw();
_painter->Paint(*_drawingEngine);
_drawingEngine->EndDraw();
sprite_position_tween_restore();
// Note: It's important to call UpdateWindows after restoring the sprite positions, not in between,
// otherwise the window updates to positions of sprites could be reverted.
// This can be observed when changing ride settings using the mouse wheel that removes all
// vehicles and peeps from the ride: it can freeze the game.
_drawingEngine->UpdateWindows();
}
}
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 != INTRO_STATE_NONE)
{
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,
});
}
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());
}
}
}
delete scanner;
}
#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
};
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(int32_t cursor)
2018-02-01 18:49:14 +01:00
{
GetContext()->GetUiContext()->SetCursor(static_cast<CURSOR_ID>(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);
}
2018-06-22 23:25:16 +02:00
rct_window* context_show_error(rct_string_id title, rct_string_id message)
2018-02-01 18:49:14 +01:00
{
auto windowManager = GetContext()->GetUiContext()->GetWindowManager();
return windowManager->ShowError(title, message);
}
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
}
bool platform_place_string_on_clipboard(utf8* target)
{
return GetContext()->GetUiContext()->SetClipboardText(target);
}
2017-12-02 00:48:46 +01:00
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());
}