Merge pull request #22301 from AaronVanGeffen/more-progress-bars

Add progress bars to loading saved games and scenarios
This commit is contained in:
Aaron van Geffen 2024-07-16 20:04:21 +02:00 committed by GitHub
commit 1d361a07e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 153 additions and 50 deletions

View file

@ -3720,6 +3720,9 @@ STR_6645 :Makes some UI elements bigger so they are easier to click or tap.
STR_6646 :Author: {STRING} STR_6646 :Author: {STRING}
STR_6647 :Authors: {STRING} STR_6647 :Authors: {STRING}
STR_6648 :Loading plugin engine… STR_6648 :Loading plugin engine…
STR_6649 :Loading scenario…
STR_6650 :Loading saved game…
STR_6651 :{STRING} ({COMMA32}%)
############# #############
# Scenarios # # Scenarios #

View file

@ -4,6 +4,7 @@
- Feature: [#20832] The ride music tab now shows a track listing for the current music style. - Feature: [#20832] The ride music tab now shows a track listing for the current music style.
- Feature: [#22172] [Plugin] Expose ride satisfaction ratings to the plugin API. - Feature: [#22172] [Plugin] Expose ride satisfaction ratings to the plugin API.
- Feature: [#22213] [Plugin] Allow plugins to focus on textboxes in custom windows. - Feature: [#22213] [Plugin] Allow plugins to focus on textboxes in custom windows.
- Feature: [#22301] Loading save games or scenarios now indicates loading progress.
- Feature: [OpenMusic#54] Added Progressive ride music style (feat. Approaching Nirvana). - Feature: [OpenMusic#54] Added Progressive ride music style (feat. Approaching Nirvana).
- Change: [#22230] The plugin/script engine is now initialised off the main thread. - Change: [#22230] The plugin/script engine is now initialised off the main thread.
- Change: [#22251] Hide author info in the scenery window unless debug tools are active. - Change: [#22251] Hide author info in the scenery window unless debug tools are active.

View file

@ -25,6 +25,7 @@
#include <openrct2/entity/EntityRegistry.h> #include <openrct2/entity/EntityRegistry.h>
#include <openrct2/interface/Viewport.h> #include <openrct2/interface/Viewport.h>
#include <openrct2/interface/Window.h> #include <openrct2/interface/Window.h>
#include <openrct2/localisation/StringIds.h>
#include <openrct2/management/NewsItem.h> #include <openrct2/management/NewsItem.h>
#include <openrct2/object/ObjectManager.h> #include <openrct2/object/ObjectManager.h>
#include <openrct2/scenario/ScenarioRepository.h> #include <openrct2/scenario/ScenarioRepository.h>
@ -48,6 +49,7 @@ namespace OpenRCT2::Title
std::unique_ptr<TitleSequence> _sequence; std::unique_ptr<TitleSequence> _sequence;
int32_t _position = 0; int32_t _position = 0;
int32_t _waitCounter = 0; int32_t _waitCounter = 0;
bool _initialLoadCommand = true;
int32_t _previousWindowWidth = 0; int32_t _previousWindowWidth = 0;
int32_t _previousWindowHeight = 0; int32_t _previousWindowHeight = 0;
@ -207,6 +209,7 @@ namespace OpenRCT2::Title
{ {
_position = 0; _position = 0;
_waitCounter = 0; _waitCounter = 0;
_initialLoadCommand = true;
} }
void Seek(int32_t targetPosition) override void Seek(int32_t targetPosition) override
@ -280,6 +283,20 @@ namespace OpenRCT2::Title
return _position != entryPosition; return _position != entryPosition;
} }
void ReportProgress(uint8_t progress)
{
if (!_initialLoadCommand)
return;
if (progress == 0)
GetContext()->OpenProgress(STR_LOADING_TITLE_SEQUENCE);
GetContext()->SetProgress(progress, 100, STR_STRING_M_PERCENT, true);
if (progress == 100)
GetContext()->CloseProgress();
}
bool LoadParkFromFile(const u8string& path) bool LoadParkFromFile(const u8string& path)
{ {
LOG_VERBOSE("TitleSequencePlayer::LoadParkFromFile(%s)", path.c_str()); LOG_VERBOSE("TitleSequencePlayer::LoadParkFromFile(%s)", path.c_str());
@ -294,24 +311,39 @@ namespace OpenRCT2::Title
} }
else else
{ {
// Inhibit viewport rendering while we're loading
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, true);
ReportProgress(0);
auto parkImporter = ParkImporter::Create(path); auto parkImporter = ParkImporter::Create(path);
auto result = parkImporter->Load(path); auto result = parkImporter->Load(path);
ReportProgress(10);
auto& objectManager = GetContext()->GetObjectManager(); auto& objectManager = GetContext()->GetObjectManager();
objectManager.LoadObjects(result.RequiredObjects); objectManager.LoadObjects(result.RequiredObjects, true);
ReportProgress(90);
// TODO: Have a separate GameState and exchange once loaded. // TODO: Have a separate GameState and exchange once loaded.
auto& gameState = GetGameState(); auto& gameState = GetGameState();
parkImporter->Import(gameState); parkImporter->Import(gameState);
ReportProgress(100);
MapAnimationAutoCreate(); MapAnimationAutoCreate();
} }
PrepareParkForPlayback(); PrepareParkForPlayback();
_initialLoadCommand = false;
success = true; success = true;
} }
catch (const std::exception&) catch (const std::exception&)
{ {
Console::Error::WriteLine("Unable to load park: %s", path.c_str()); Console::Error::WriteLine("Unable to load park: %s", path.c_str());
GetContext()->CloseProgress();
} }
// Reset viewport rendering inhibition
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, false);
gLoadKeepWindowsOpen = false; gLoadKeepWindowsOpen = false;
return success; return success;
} }
@ -334,26 +366,40 @@ namespace OpenRCT2::Title
} }
else else
{ {
// Inhibit viewport rendering while we're loading
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, true);
ReportProgress(0);
bool isScenario = ParkImporter::ExtensionIsScenario(hintPath); bool isScenario = ParkImporter::ExtensionIsScenario(hintPath);
auto parkImporter = ParkImporter::Create(hintPath); auto parkImporter = ParkImporter::Create(hintPath);
auto result = parkImporter->LoadFromStream(stream, isScenario); auto result = parkImporter->LoadFromStream(stream, isScenario);
ReportProgress(30);
auto& objectManager = GetContext()->GetObjectManager(); auto& objectManager = GetContext()->GetObjectManager();
objectManager.LoadObjects(result.RequiredObjects); objectManager.LoadObjects(result.RequiredObjects, true);
ReportProgress(70);
// TODO: Have a separate GameState and exchange once loaded. // TODO: Have a separate GameState and exchange once loaded.
auto& gameState = GetGameState(); auto& gameState = GetGameState();
parkImporter->Import(gameState); parkImporter->Import(gameState);
ReportProgress(100);
MapAnimationAutoCreate(); MapAnimationAutoCreate();
} }
PrepareParkForPlayback(); PrepareParkForPlayback();
_initialLoadCommand = false;
success = true; success = true;
} }
catch (const std::exception&) catch (const std::exception&)
{ {
Console::Error::WriteLine("Unable to load park: %s", hintPath.c_str()); Console::Error::WriteLine("Unable to load park: %s", hintPath.c_str());
GetContext()->CloseProgress();
} }
// Reset viewport rendering inhibition
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, false);
gLoadKeepWindowsOpen = false; gLoadKeepWindowsOpen = false;
return success; return success;
} }

View file

@ -48,10 +48,6 @@ static Widget _mainWidgets[] = {
void OnDraw(DrawPixelInfo& dpi) override void OnDraw(DrawPixelInfo& dpi) override
{ {
// Skip viewport render during preloader
if (GetContext()->GetActiveScene() == GetContext()->GetPreloaderScene())
return;
ViewportRender(dpi, viewport, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } }); ViewportRender(dpi, viewport, { { dpi.x, dpi.y }, { dpi.x + dpi.width, dpi.y + dpi.height } });
} }

View file

@ -656,13 +656,24 @@ namespace OpenRCT2
ContextOpenIntent(&intent); ContextOpenIntent(&intent);
} }
void SetProgress(uint32_t currentProgress, uint32_t totalCount, StringId format = STR_NONE) override void SetProgress(
uint32_t currentProgress, uint32_t totalCount, StringId format = STR_NONE, bool forceDraw = false) override
{ {
auto intent = Intent(INTENT_ACTION_PROGRESS_SET); auto intent = Intent(INTENT_ACTION_PROGRESS_SET);
intent.PutExtra(INTENT_EXTRA_PROGRESS_OFFSET, currentProgress); intent.PutExtra(INTENT_EXTRA_PROGRESS_OFFSET, currentProgress);
intent.PutExtra(INTENT_EXTRA_PROGRESS_TOTAL, totalCount); intent.PutExtra(INTENT_EXTRA_PROGRESS_TOTAL, totalCount);
intent.PutExtra(INTENT_EXTRA_STRING_ID, format); intent.PutExtra(INTENT_EXTRA_STRING_ID, format);
ContextOpenIntent(&intent); ContextOpenIntent(&intent);
// Ideally, we'd force a redraw at all times at this point. OpenGL has to be directed
// from the main thread, though, so this cannot be invoked when off main thread.
// It's fine (and indeed useful!) for synchronous calls, so we keep it as an option.
if (!gOpenRCT2Headless && forceDraw)
{
_uiContext->ProcessMessages();
WindowInvalidateByClass(WindowClass::ProgressWindow);
Draw();
}
} }
void CloseProgress() override void CloseProgress() override
@ -755,18 +766,30 @@ namespace OpenRCT2
parkImporter = ParkImporter::CreateS6(*_objectRepository); parkImporter = ParkImporter::CreateS6(*_objectRepository);
} }
// Inhibit viewport rendering while we're loading
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, true);
OpenProgress(asScenario ? STR_LOADING_SCENARIO : STR_LOADING_SAVED_GAME);
SetProgress(0, 100, STR_STRING_M_PERCENT, true);
auto result = parkImporter->LoadFromStream(stream, info.Type == FILE_TYPE::SCENARIO, false, path.c_str()); auto result = parkImporter->LoadFromStream(stream, info.Type == FILE_TYPE::SCENARIO, false, path.c_str());
SetProgress(10, 100, STR_STRING_M_PERCENT, true);
// From this point onwards the currently loaded park will be corrupted if loading fails // From this point onwards the currently loaded park will be corrupted if loading fails
// so reload the title screen if that happens. // so reload the title screen if that happens.
loadTitleScreenFirstOnFail = true; loadTitleScreenFirstOnFail = true;
GameUnloadScripts(); GameUnloadScripts();
_objectManager->LoadObjects(result.RequiredObjects); _objectManager->LoadObjects(result.RequiredObjects, true);
SetProgress(90, 100, STR_STRING_M_PERCENT, true);
// TODO: Have a separate GameState and exchange once loaded. // TODO: Have a separate GameState and exchange once loaded.
auto& gameState = ::GetGameState(); auto& gameState = ::GetGameState();
parkImporter->Import(gameState); parkImporter->Import(gameState);
SetProgress(100, 100, STR_STRING_M_PERCENT, true);
// Reset viewport rendering inhibition
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, false);
gScenarioSavePath = path; gScenarioSavePath = path;
gCurrentLoadedPath = path; gCurrentLoadedPath = path;
@ -841,6 +864,7 @@ namespace OpenRCT2
windowManager->ShowError(STR_PARK_USES_FALLBACK_IMAGES_WARNING, STR_EMPTY, Formatter()); windowManager->ShowError(STR_PARK_USES_FALLBACK_IMAGES_WARNING, STR_EMPTY, Formatter());
} }
CloseProgress();
return true; return true;
} }
catch (const ObjectLoadException& e) catch (const ObjectLoadException& e)
@ -916,6 +940,8 @@ namespace OpenRCT2
Console::Error::WriteLine(e.what()); Console::Error::WriteLine(e.what());
} }
CloseProgress();
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, false);
return false; return false;
} }

View file

@ -160,7 +160,9 @@ namespace OpenRCT2
virtual void DisposeDrawingEngine() = 0; virtual void DisposeDrawingEngine() = 0;
virtual void OpenProgress(StringId captionStringId) = 0; virtual void OpenProgress(StringId captionStringId) = 0;
virtual void SetProgress(uint32_t currentProgress, uint32_t totalCount, StringId format = STR_NONE) = 0; virtual void SetProgress(
uint32_t currentProgress, uint32_t totalCount, StringId format = STR_NONE, bool forceDraw = false)
= 0;
virtual void CloseProgress() = 0; virtual void CloseProgress() = 0;
virtual bool LoadParkFromFile(const u8string& path, bool loadTitleScreenOnFail = false, bool asScenario = false) = 0; virtual bool LoadParkFromFile(const u8string& path, bool loadTitleScreenOnFail = false, bool asScenario = false) = 0;

View file

@ -927,6 +927,9 @@ void ViewportRotateAll(int32_t direction)
*/ */
void ViewportRender(DrawPixelInfo& dpi, const Viewport* viewport, const ScreenRect& screenRect) void ViewportRender(DrawPixelInfo& dpi, const Viewport* viewport, const ScreenRect& screenRect)
{ {
if (viewport->flags & VIEWPORT_FLAG_RENDERING_INHIBITED)
return;
auto [topLeft, bottomRight] = screenRect; auto [topLeft, bottomRight] = screenRect;
if (bottomRight.x <= viewport->pos.x) if (bottomRight.x <= viewport->pos.x)
@ -1018,6 +1021,9 @@ static void ViewportPaint(const Viewport* viewport, DrawPixelInfo& dpi, const Sc
PROFILED_FUNCTION(); PROFILED_FUNCTION();
const uint32_t viewFlags = viewport->flags; const uint32_t viewFlags = viewport->flags;
if (viewFlags & VIEWPORT_FLAG_RENDERING_INHIBITED)
return;
uint32_t width = screenRect.GetWidth(); uint32_t width = screenRect.GetWidth();
uint32_t height = screenRect.GetHeight(); uint32_t height = screenRect.GetHeight();
const uint32_t bitmask = viewport->zoom >= ZoomLevel{ 0 } ? 0xFFFFFFFF & (viewport->zoom.ApplyTo(0xFFFFFFFF)) : 0xFFFFFFFF; const uint32_t bitmask = viewport->zoom >= ZoomLevel{ 0 } ? 0xFFFFFFFF & (viewport->zoom.ApplyTo(0xFFFFFFFF)) : 0xFFFFFFFF;

View file

@ -66,6 +66,7 @@ enum : uint32_t
VIEWPORT_FLAG_INVISIBLE_SUPPORTS = (1u << 29), VIEWPORT_FLAG_INVISIBLE_SUPPORTS = (1u << 29),
VIEWPORT_FLAG_INDEPEDENT_ROTATION = (1u << 30), VIEWPORT_FLAG_INDEPEDENT_ROTATION = (1u << 30),
VIEWPORT_FLAG_RENDERING_INHIBITED = (1u << 31),
}; };
enum class VisibilityKind enum class VisibilityKind

View file

@ -101,6 +101,19 @@ void WindowVisitEach(std::function<void(WindowBase*)> func)
} }
} }
void WindowSetFlagForAllViewports(uint32_t viewportFlag, bool enabled)
{
WindowVisitEach([&](WindowBase* w) {
if (w->viewport != nullptr)
{
if (enabled)
w->viewport->flags |= viewportFlag;
else
w->viewport->flags &= ~viewportFlag;
}
});
}
/** /**
* *
* rct2: 0x006ED7B0 * rct2: 0x006ED7B0

View file

@ -491,6 +491,8 @@ extern bool gDisableErrorWindowSound;
std::list<std::shared_ptr<WindowBase>>::iterator WindowGetIterator(const WindowBase* w); std::list<std::shared_ptr<WindowBase>>::iterator WindowGetIterator(const WindowBase* w);
void WindowVisitEach(std::function<void(WindowBase*)> func); void WindowVisitEach(std::function<void(WindowBase*)> func);
void WindowSetFlagForAllViewports(uint32_t viewportFlag, bool enabled);
void WindowDispatchUpdateAll(); void WindowDispatchUpdateAll();
void WindowUpdateAllViewports(); void WindowUpdateAllViewports();
void WindowUpdateAll(); void WindowUpdateAll();

View file

@ -1685,6 +1685,9 @@ enum : StringId
STR_STRING_M_OF_N_KIB = 6643, STR_STRING_M_OF_N_KIB = 6643,
STR_LOADING_PLUGIN_ENGINE = 6648, STR_LOADING_PLUGIN_ENGINE = 6648,
STR_LOADING_SCENARIO = 6649,
STR_LOADING_SAVED_GAME = 6650,
STR_STRING_M_PERCENT = 6651,
// Have to include resource strings (from scenarios and objects) for the time being now that language is partially working // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working
/* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings /* MAX_STR_COUNT = 32768 */ // MAX_STR_COUNT - upper limit for number of strings, not the current count strings

View file

@ -14,6 +14,7 @@
#include "../ParkImporter.h" #include "../ParkImporter.h"
#include "../audio/audio.h" #include "../audio/audio.h"
#include "../core/Console.hpp" #include "../core/Console.hpp"
#include "../core/JobPool.h"
#include "../core/Memory.hpp" #include "../core/Memory.hpp"
#include "../localisation/StringIds.h" #include "../localisation/StringIds.h"
#include "../ride/Ride.h" #include "../ride/Ride.h"
@ -38,6 +39,8 @@
#include <thread> #include <thread>
#include <unordered_set> #include <unordered_set>
using namespace OpenRCT2;
/** /**
* Represents an object that is to be loaded or is loaded and ready * Represents an object that is to be loaded or is loaded and ready
* to be placed in an object list. * to be placed in an object list.
@ -183,13 +186,13 @@ public:
return RepositoryItemToObject(ori, slot); return RepositoryItemToObject(ori, slot);
} }
void LoadObjects(const ObjectList& objectList) override void LoadObjects(const ObjectList& objectList, const bool reportProgress) override
{ {
// Find all the required objects // Find all the required objects
auto requiredObjects = GetRequiredObjects(objectList); auto requiredObjects = GetRequiredObjects(objectList);
// Load the required objects // Load the required objects
LoadObjects(requiredObjects); LoadObjects(requiredObjects, reportProgress);
// Update indices. // Update indices.
UpdateSceneryGroupIndexes(); UpdateSceneryGroupIndexes();
@ -556,31 +559,17 @@ private:
return requiredObjects; return requiredObjects;
} }
template<typename T, typename TFunc> static void ParallelFor(const std::vector<T>& items, TFunc func) void ReportProgress(size_t numLoaded, size_t numRequired)
{ {
auto partitions = std::thread::hardware_concurrency(); constexpr auto kObjectLoadMinProgress = 10;
auto partitionSize = (items.size() + (partitions - 1)) / partitions; constexpr auto kObjectLoadMaxProgress = 90;
std::vector<std::thread> threads; constexpr auto kObjectLoadProgressRange = kObjectLoadMaxProgress - kObjectLoadMinProgress;
for (size_t n = 0; n < partitions; n++)
{ const auto currentProgress = kObjectLoadMinProgress + (numLoaded * kObjectLoadProgressRange / numRequired);
auto begin = n * partitionSize; OpenRCT2::GetContext()->SetProgress(static_cast<uint32_t>(currentProgress), 100, STR_STRING_M_PERCENT, true);
auto end = std::min(items.size(), begin + partitionSize);
threads.emplace_back(
[func](size_t pbegin, size_t pend) {
for (size_t i = pbegin; i < pend; i++)
{
func(i);
}
},
begin, end);
}
for (auto& t : threads)
{
t.join();
}
} }
void LoadObjects(std::vector<ObjectToLoad>& requiredObjects) void LoadObjects(std::vector<ObjectToLoad>& requiredObjects, bool reportProgress)
{ {
std::vector<Object*> objects; std::vector<Object*> objects;
std::vector<Object*> newLoadedObjects; std::vector<Object*> newLoadedObjects;
@ -607,11 +596,11 @@ private:
std::sort(objectsToLoad.begin(), objectsToLoad.end()); std::sort(objectsToLoad.begin(), objectsToLoad.end());
objectsToLoad.erase(std::unique(objectsToLoad.begin(), objectsToLoad.end()), objectsToLoad.end()); objectsToLoad.erase(std::unique(objectsToLoad.begin(), objectsToLoad.end()), objectsToLoad.end());
// Load the objects. // Prepare for loading objects multi-threaded
auto numProcessed = 0;
auto numRequired = objectsToLoad.size();
std::mutex commonMutex; std::mutex commonMutex;
ParallelFor(objectsToLoad, [&](size_t i) { auto loadSingleObject = [&](const ObjectRepositoryItem* requiredObject) {
const auto* requiredObject = objectsToLoad[i];
// Object requires to be loaded, if the object successfully loads it will register it // Object requires to be loaded, if the object successfully loads it will register it
// as a loaded object otherwise placed into the badObjects list. // as a loaded object otherwise placed into the badObjects list.
auto newObject = _objectRepository.LoadObject(requiredObject); auto newObject = _objectRepository.LoadObject(requiredObject);
@ -628,7 +617,24 @@ private:
// Connect the ori to the registered object // Connect the ori to the registered object
_objectRepository.RegisterLoadedObject(requiredObject, std::move(newObject)); _objectRepository.RegisterLoadedObject(requiredObject, std::move(newObject));
} }
});
numProcessed++;
};
auto completionFn = [&]() {
if (reportProgress && (numProcessed % 100) == 0)
ReportProgress(numProcessed, numRequired);
};
// Dispatch loading the objects
JobPool jobs{};
for (auto* object : objectsToLoad)
{
jobs.AddTask([object, &loadSingleObject]() { loadSingleObject(object); }, completionFn);
}
// Wait until all jobs are fully completed
jobs.Join();
// Assign the loaded objects to the required objects // Assign the loaded objects to the required objects
for (auto& requiredObject : requiredObjects) for (auto& requiredObject : requiredObjects)

View file

@ -36,7 +36,7 @@ struct IObjectManager
virtual Object* LoadObject(const RCTObjectEntry* entry) = 0; virtual Object* LoadObject(const RCTObjectEntry* entry) = 0;
virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor) = 0; virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor) = 0;
virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor, ObjectEntryIndex slot) = 0; virtual Object* LoadObject(const ObjectEntryDescriptor& descriptor, ObjectEntryIndex slot) = 0;
virtual void LoadObjects(const ObjectList& entries) = 0; virtual void LoadObjects(const ObjectList& entries, const bool reportProgress = false) = 0;
virtual void UnloadObjects(const std::vector<ObjectEntryDescriptor>& entries) = 0; virtual void UnloadObjects(const std::vector<ObjectEntryDescriptor>& entries) = 0;
virtual void UnloadAllTransient() = 0; virtual void UnloadAllTransient() = 0;
virtual void UnloadAll() = 0; virtual void UnloadAll() = 0;

View file

@ -41,6 +41,7 @@ void PreloaderScene::Load()
gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE); gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE);
ViewportInitAll(); ViewportInitAll();
ContextOpenWindow(WindowClass::MainWindow); ContextOpenWindow(WindowClass::MainWindow);
WindowSetFlagForAllViewports(VIEWPORT_FLAG_RENDERING_INHIBITED, true);
WindowResizeGui(ContextGetWidth(), ContextGetHeight()); WindowResizeGui(ContextGetWidth(), ContextGetHeight());
// Reset screen // Reset screen

View file

@ -109,19 +109,10 @@ void TitleScene::Load()
gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE); gameStateInitAll(GetGameState(), DEFAULT_MAP_SIZE);
ViewportInitAll(); ViewportInitAll();
ContextOpenWindow(WindowClass::MainWindow); ContextOpenWindow(WindowClass::MainWindow);
CreateWindows();
GetContext().OpenProgress(STR_LOADING_TITLE_SEQUENCE);
TitleInitialise(); TitleInitialise();
OpenRCT2::Audio::PlayTitleMusic(); OpenRCT2::Audio::PlayTitleMusic();
if (gOpenRCT2ShowChangelog)
{
gOpenRCT2ShowChangelog = false;
ContextOpenWindow(WindowClass::Changelog);
}
if (_sequencePlayer != nullptr) if (_sequencePlayer != nullptr)
{ {
// Force the title sequence to load / update so we // Force the title sequence to load / update so we
@ -131,7 +122,13 @@ void TitleScene::Load()
_sequencePlayer->Update(); _sequencePlayer->Update();
} }
GetContext().CloseProgress(); CreateWindows();
if (gOpenRCT2ShowChangelog)
{
gOpenRCT2ShowChangelog = false;
ContextOpenWindow(WindowClass::Changelog);
}
LOG_VERBOSE("TitleScene::Load() finished"); LOG_VERBOSE("TitleScene::Load() finished");
} }