From 1fc2312abd38c00154087bae22e24067ff9bd4c0 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Sun, 19 Jun 2022 00:18:18 +0200 Subject: [PATCH] Move most command logic from sequence player This commit also moves everything related to the OpenRCT2::Title namespace. Can be compiled with ENABLE_SCRIPTING disabled. --- src/openrct2-ui/UiContext.cpp | 2 +- src/openrct2-ui/title/TitleSequencePlayer.cpp | 703 ++++++------- src/openrct2-ui/title/TitleSequencePlayer.h | 7 +- src/openrct2/title/Command/LoadPark.cpp | 3 +- src/openrct2/title/Command/LoadScenario.cpp | 3 +- src/openrct2/title/Command/RotateView.cpp | 11 +- src/openrct2/title/Command/SetLocation.cpp | 20 +- src/openrct2/title/Command/SetSpeed.cpp | 6 +- src/openrct2/title/Command/SetZoom.cpp | 9 +- src/openrct2/title/Command/Wait.cpp | 7 +- src/openrct2/title/TitleSequence.cpp | 993 +++++++++--------- src/openrct2/title/TitleSequenceManager.cpp | 6 +- 12 files changed, 852 insertions(+), 918 deletions(-) diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index 04d9dc39bf..858fa3dd50 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -710,7 +710,7 @@ public: { auto context = GetContext(); auto gameState = context->GetGameState(); - _titleSequencePlayer = CreateTitleSequencePlayer(*gameState); + _titleSequencePlayer = OpenRCT2::Title::CreateTitleSequencePlayer(*gameState); } return _titleSequencePlayer.get(); } diff --git a/src/openrct2-ui/title/TitleSequencePlayer.cpp b/src/openrct2-ui/title/TitleSequencePlayer.cpp index 9101fb6aef..91c6c37a89 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.cpp +++ b/src/openrct2-ui/title/TitleSequencePlayer.cpp @@ -39,489 +39,376 @@ #include #include #include +#include -using namespace OpenRCT2; - -class TitleSequencePlayer final : public ITitleSequencePlayer +namespace OpenRCT2::Title { -private: - GameState& _gameState; - - std::unique_ptr _sequence; - int32_t _position = 0; - int32_t _waitCounter = 0; - - int32_t _lastScreenWidth = 0; - int32_t _lastScreenHeight = 0; - CoordsXY _viewCentreLocation = {}; - -public: - explicit TitleSequencePlayer(GameState& gameState) - : _gameState(gameState) + class TitleSequencePlayer final : public ITitleSequencePlayer { - } + private: + GameState& _gameState; - ~TitleSequencePlayer() override - { - Eject(); - } + std::unique_ptr _sequence; + int32_t _position = 0; + int32_t _waitCounter = 0; - int32_t GetCurrentPosition() const override - { - return _position; - } + int32_t _lastScreenWidth = 0; + int32_t _lastScreenHeight = 0; + CoordsXY _viewCentreLocation = {}; - void Eject() override - { - _sequence = nullptr; - } - - bool Begin(size_t titleSequenceId) override - { - size_t numSequences = TitleSequenceManager::GetCount(); - if (titleSequenceId >= numSequences) + public: + explicit TitleSequencePlayer(GameState& gameState) + : _gameState(gameState) { - return false; } - auto seqItem = TitleSequenceManager::GetItem(titleSequenceId); - auto sequence = LoadTitleSequence(seqItem->Path); - if (sequence == nullptr) + ~TitleSequencePlayer() override { - return false; + Eject(); } - Eject(); - _sequence = std::move(sequence); - - Reset(); - return true; - } - - bool Update() override - { - int32_t entryPosition = _position; - FixViewLocation(); - - if (_sequence == nullptr) + int32_t GetCurrentPosition() const override { - SetViewLocation(TileCoordsXY(75, 75).ToCoordsXY()); - return false; + return _position; } - // Check that position is valid - if (_position >= static_cast(_sequence->Commands.size())) + void Eject() override { - _position = 0; - return false; + _sequence = nullptr; } - // Don't execute next command until we are done with the current wait command - if (_waitCounter != 0) + bool Begin(size_t titleSequenceId) override { - _waitCounter--; - if (_waitCounter == 0) + size_t numSequences = TitleSequenceManager::GetCount(); + if (titleSequenceId >= numSequences) { - const auto& command = _sequence->Commands[_position]; - if (command.Type == TitleScript::Wait) - { - IncrementPosition(); - } + return false; } + + auto seqItem = TitleSequenceManager::GetItem(titleSequenceId); + auto sequence = LoadTitleSequence(seqItem->Path); + if (sequence == nullptr) + { + return false; + } + + Eject(); + _sequence = std::move(sequence); + + Reset(); + return true; } - else + + bool Update() override { + FixViewLocation(); + + if (_sequence == nullptr) + { + return false; + } + + // Run commands in order, until we reach one that is not instantly done + int32_t entryPosition = _position; while (true) { - const auto& command = _sequence->Commands[_position]; - if (ExecuteCommand(command)) + auto& currentCommand = _sequence->Commands[_position]; + try { - if (command.Type == TitleScript::Wait) + int framesToWait = std::visit([&](auto& command) { return command(_waitCounter); }, currentCommand); + if (framesToWait > _waitCounter) { - break; + _waitCounter++; + return true; } - if (command.Type != TitleScript::Restart) + + // TODO: Make the loading interface simpler so these blocks can be moved to their respective command classes + if (std::holds_alternative(currentCommand)) { - IncrementPosition(); + bool loadSuccess = false; + const auto saveIndex = std::get(currentCommand).SaveIndex; + auto parkHandle = TitleSequenceGetParkHandle(*_sequence, saveIndex); + if (parkHandle != nullptr) + { + game_notify_map_change(); + loadSuccess = LoadParkFromStream(parkHandle->Stream.get(), parkHandle->HintPath); + } + if (!loadSuccess) + { + if (_sequence->Saves.size() > saveIndex) + { + const auto& path = _sequence->Saves[saveIndex]; + throw std::domain_error("Failed to load: \"" + path + "\" for the title sequence."); + } + + throw std::out_of_range("Failed to load park; index out of range."); + } + + game_notify_map_changed(); } - if (_position == entryPosition) + else if (std::holds_alternative(currentCommand)) { - Console::Error::WriteLine("Infinite loop detected in title sequence."); - Console::Error::WriteLine(" A wait command may be missing."); - return false; + auto& scenarioName = std::get(currentCommand).Scenario; + bool loadSuccess = false; + auto scenario = GetScenarioRepository()->GetByInternalName(scenarioName); + if (scenario != nullptr) + { + game_notify_map_change(); + loadSuccess = LoadParkFromFile(scenario->path); + } + + if (!loadSuccess) + { + auto message = std::string("Failed to load: \"") + scenarioName + " for the title sequence."; + throw std::domain_error(message); + } + + game_notify_map_changed(); } } - else + catch (std::exception& e) { - if (!SkipToNextLoadCommand() || _position == entryPosition) - { - Console::Error::WriteLine("Unable to load any parks from %s.", _sequence->Name.c_str()); - return false; - } + const char* commandName = std::visit( + [](auto&& command) { return std::decay::type::Name; }, currentCommand); + Console::Error::WriteLine("%s (command %i) failed with error: %s", commandName, _position, e.what()); + Console::Error::WriteLine(" Skipping to the next command."); + } + + IncrementPosition(); + + if (_position == entryPosition) + { + Console::Error::WriteLine("Infinite loop detected in title sequence."); + Console::Error::WriteLine(" A wait command may be missing."); + return false; } } } - return true; - } - void Reset() override - { - _position = 0; - _waitCounter = 0; - } - - void Seek(int32_t targetPosition) override - { - if (targetPosition < 0 || targetPosition >= static_cast(_sequence->Commands.size())) - { - throw std::runtime_error("Invalid position."); - } - if (_position >= targetPosition) - { - Reset(); - } - - if (_sequence->Commands[targetPosition].Type == TitleScript::Restart) - { - targetPosition = 0; - } - // Set position to the last LOAD command before target position - for (int32_t i = targetPosition; i >= 0; i--) - { - const TitleCommand& command = _sequence->Commands[i]; - if ((_position == i && _position != targetPosition) || TitleSequenceIsLoadCommand(command)) - { - // Break if we have a new load command or if we're already in the range of the correct load command - _position = i; - break; - } - } - - // Keep updating until we reach target position - gInUpdateCode = true; - - while (_position < targetPosition) - { - if (Update()) - { - _gameState.UpdateLogic(); - } - else - { - break; - } - } - - gInUpdateCode = false; - - _waitCounter = 0; - } - -private: - void IncrementPosition() - { - _position++; - if (_position >= static_cast(_sequence->Commands.size())) + void Reset() override { _position = 0; + _waitCounter = 0; } - } - bool SkipToNextLoadCommand() - { - int32_t entryPosition = _position; - const TitleCommand* command; - do + void Seek(int32_t targetPosition) override { - IncrementPosition(); - command = &_sequence->Commands[_position]; - } while (!TitleSequenceIsLoadCommand(*command) && _position != entryPosition); - return _position != entryPosition; - } - - bool ExecuteCommand(const TitleCommand& command) - { - switch (command.Type) - { - case TitleScript::End: - _waitCounter = 1; - break; - case TitleScript::Wait: - // The waitCounter is measured in 25-ms game ticks. Previously it was seconds * 40 ticks/second, now it is ms / - // 25 ms/tick - _waitCounter = std::max( - 1, command.Milliseconds / static_cast(GAME_UPDATE_TIME_MS * 1000.0f)); - break; - case TitleScript::Location: + if (targetPosition < 0 || targetPosition >= static_cast(_sequence->Commands.size())) { - auto loc = TileCoordsXY(command.Location.X, command.Location.Y).ToCoordsXY().ToTileCentre(); - SetViewLocation(loc); - break; + throw std::runtime_error("Invalid position."); } - case TitleScript::Undefined: - break; - case TitleScript::Loop: - break; - case TitleScript::EndLoop: - break; - case TitleScript::Rotate: - RotateView(command.Rotations); - break; - case TitleScript::Zoom: - SetViewZoom(ZoomLevel{ static_cast(command.Zoom) }); - break; - case TitleScript::Speed: - gGameSpeed = std::clamp(command.Speed, 1, 4); - break; - case TitleScript::Follow: - FollowSprite(command.Follow.SpriteIndex); - break; - case TitleScript::Restart: + if (_position >= targetPosition) + { Reset(); - break; - case TitleScript::Load: + } + + if (std::holds_alternative(_sequence->Commands[targetPosition])) { - bool loadSuccess = false; - uint8_t saveIndex = command.SaveIndex; - auto parkHandle = TitleSequenceGetParkHandle(*_sequence, saveIndex); - if (parkHandle != nullptr) + targetPosition = 0; + } + + // Set position to the last LOAD command before target position + for (int32_t i = targetPosition; i >= 0; i--) + { + const TitleCommand& command = _sequence->Commands[i]; + if ((_position == i && _position != targetPosition) || TitleSequenceIsLoadCommand(command)) { - game_notify_map_change(); - loadSuccess = LoadParkFromStream(parkHandle->Stream.get(), parkHandle->HintPath); + // Break if we have a new load command or if we're already in the range of the correct load command + _position = i; + break; } - if (loadSuccess) + } + + // Keep updating until we reach target position + gInUpdateCode = true; + + while (_position < targetPosition) + { + if (Update()) { - game_notify_map_changed(); + _gameState.UpdateLogic(); } else { - if (_sequence->Saves.size() > saveIndex) - { - const auto& path = _sequence->Saves[saveIndex]; - Console::Error::WriteLine("Failed to load: \"%s\" for the title sequence.", path.c_str()); - } - return false; + break; } - break; } - case TitleScript::LoadSc: + + gInUpdateCode = false; + + _waitCounter = 0; + } + + private: + void IncrementPosition() + { + _position++; + if (_position >= static_cast(_sequence->Commands.size())) { - bool loadSuccess = false; - auto scenario = GetScenarioRepository()->GetByInternalName(command.Scenario); - if (scenario != nullptr) + _position = 0; + } + _waitCounter = 0; + } + + bool SkipToNextLoadCommand() + { + int32_t entryPosition = _position; + const TitleCommand* command; + do + { + IncrementPosition(); + command = &_sequence->Commands[_position]; + } while (!TitleSequenceIsLoadCommand(*command) && _position != entryPosition); + return _position != entryPosition; + } + + void FollowSprite(EntityId spriteIndex) + { + rct_window* w = window_get_main(); + if (w != nullptr) + { + window_follow_sprite(w, spriteIndex); + } + } + + void UnfollowSprite() + { + rct_window* w = window_get_main(); + if (w != nullptr) + { + window_unfollow_sprite(w); + } + } + + bool LoadParkFromFile(const utf8* path) + { + log_verbose("TitleSequencePlayer::LoadParkFromFile(%s)", path); + bool success = false; + try + { + if (gPreviewingTitleSequenceInGame) { - game_notify_map_change(); - loadSuccess = LoadParkFromFile(scenario->path); - } - if (loadSuccess) - { - game_notify_map_changed(); + gLoadKeepWindowsOpen = true; + CloseParkSpecificWindows(); + context_load_park_from_file(path); } else { - Console::Error::WriteLine("Failed to load: \"%s\" for the title sequence.", command.Scenario); - return false; + auto parkImporter = ParkImporter::Create(path); + auto result = parkImporter->Load(path); + + auto& objectManager = GetContext()->GetObjectManager(); + objectManager.LoadObjects(result.RequiredObjects); + + parkImporter->Import(); } - break; + PrepareParkForPlayback(); + success = true; } - } - return true; - } - - void SetViewZoom(ZoomLevel zoom) - { - rct_window* w = window_get_main(); - if (w != nullptr && w->viewport != nullptr) - { - window_zoom_set(w, zoom, false); - } - } - - void RotateView(uint32_t count) - { - rct_window* w = window_get_main(); - if (w != nullptr) - { - for (uint32_t i = 0; i < count; i++) + catch (const std::exception&) { - window_rotate_camera(w, 1); + Console::Error::WriteLine("Unable to load park: %s", path); } + gLoadKeepWindowsOpen = false; + return success; } - } - void FollowSprite(EntityId spriteIndex) - { - rct_window* w = window_get_main(); - if (w != nullptr) + /** + * @param stream The stream to read the park data from. + * @param hintPath Hint path, the extension is grabbed to determine what importer to use. + */ + bool LoadParkFromStream(OpenRCT2::IStream* stream, const std::string& hintPath) { - window_follow_sprite(w, spriteIndex); - } - } - - void UnfollowSprite() - { - rct_window* w = window_get_main(); - if (w != nullptr) - { - window_unfollow_sprite(w); - } - } - - bool LoadParkFromFile(const utf8* path) - { - log_verbose("TitleSequencePlayer::LoadParkFromFile(%s)", path); - bool success = false; - try - { - if (gPreviewingTitleSequenceInGame) + log_verbose("TitleSequencePlayer::LoadParkFromStream(%s)", hintPath.c_str()); + bool success = false; + try { - gLoadKeepWindowsOpen = true; - CloseParkSpecificWindows(); - context_load_park_from_file(path); + if (gPreviewingTitleSequenceInGame) + { + gLoadKeepWindowsOpen = true; + CloseParkSpecificWindows(); + context_load_park_from_stream(stream); + } + else + { + bool isScenario = ParkImporter::ExtensionIsScenario(hintPath); + auto parkImporter = ParkImporter::Create(hintPath); + auto result = parkImporter->LoadFromStream(stream, isScenario); + + auto& objectManager = GetContext()->GetObjectManager(); + objectManager.LoadObjects(result.RequiredObjects); + + parkImporter->Import(); + } + PrepareParkForPlayback(); + success = true; } - else + catch (const std::exception&) { - auto parkImporter = ParkImporter::Create(path); - auto result = parkImporter->Load(path); - - auto& objectManager = GetContext()->GetObjectManager(); - objectManager.LoadObjects(result.RequiredObjects); - - parkImporter->Import(); + Console::Error::WriteLine("Unable to load park: %s", hintPath.c_str()); } - PrepareParkForPlayback(); - success = true; + gLoadKeepWindowsOpen = false; + return success; } - catch (const std::exception&) - { - Console::Error::WriteLine("Unable to load park: %s", path); - } - gLoadKeepWindowsOpen = false; - return success; - } - /** - * @param stream The stream to read the park data from. - * @param hintPath Hint path, the extension is grabbed to determine what importer to use. - */ - bool LoadParkFromStream(OpenRCT2::IStream* stream, const std::string& hintPath) - { - log_verbose("TitleSequencePlayer::LoadParkFromStream(%s)", hintPath.c_str()); - bool success = false; - try + void CloseParkSpecificWindows() { - if (gPreviewingTitleSequenceInGame) + window_close_by_class(WC_CONSTRUCT_RIDE); + window_close_by_class(WC_DEMOLISH_RIDE_PROMPT); + window_close_by_class(WC_EDITOR_INVENTION_LIST_DRAG); + window_close_by_class(WC_EDITOR_INVENTION_LIST); + window_close_by_class(WC_EDITOR_OBJECT_SELECTION); + window_close_by_class(WC_EDITOR_OBJECTIVE_OPTIONS); + window_close_by_class(WC_EDITOR_SCENARIO_OPTIONS); + window_close_by_class(WC_FINANCES); + window_close_by_class(WC_FIRE_PROMPT); + window_close_by_class(WC_GUEST_LIST); + window_close_by_class(WC_INSTALL_TRACK); + window_close_by_class(WC_PEEP); + window_close_by_class(WC_RIDE); + window_close_by_class(WC_RIDE_CONSTRUCTION); + window_close_by_class(WC_RIDE_LIST); + window_close_by_class(WC_SCENERY); + window_close_by_class(WC_STAFF); + window_close_by_class(WC_TRACK_DELETE_PROMPT); + window_close_by_class(WC_TRACK_DESIGN_LIST); + window_close_by_class(WC_TRACK_DESIGN_PLACE); + } + + void PrepareParkForPlayback() + { + auto windowManager = GetContext()->GetUiContext()->GetWindowManager(); + windowManager->SetMainView(gSavedView, gSavedViewZoom, gSavedViewRotation); + ResetEntitySpatialIndices(); + reset_all_sprite_quadrant_placements(); + auto intent = Intent(INTENT_ACTION_REFRESH_NEW_RIDES); + context_broadcast_intent(&intent); + scenery_set_default_placement_configuration(); + News::InitQueue(); + load_palette(); + gScreenAge = 0; + gGamePaused = false; + gGameSpeed = 1; + } + + /** + * Fixes the view location for when the game window has changed size. + */ + void FixViewLocation() + { + rct_window* w = window_get_main(); + if (w != nullptr && w->viewport_smart_follow_sprite.IsNull()) { - gLoadKeepWindowsOpen = true; - CloseParkSpecificWindows(); - context_load_park_from_stream(stream); - } - else - { - bool isScenario = ParkImporter::ExtensionIsScenario(hintPath); - auto parkImporter = ParkImporter::Create(hintPath); - auto result = parkImporter->LoadFromStream(stream, isScenario); - - auto& objectManager = GetContext()->GetObjectManager(); - objectManager.LoadObjects(result.RequiredObjects); - - parkImporter->Import(); - } - PrepareParkForPlayback(); - success = true; - } - catch (const std::exception&) - { - Console::Error::WriteLine("Unable to load park: %s", hintPath.c_str()); - } - gLoadKeepWindowsOpen = false; - return success; - } - - void CloseParkSpecificWindows() - { - window_close_by_class(WC_CONSTRUCT_RIDE); - window_close_by_class(WC_DEMOLISH_RIDE_PROMPT); - window_close_by_class(WC_EDITOR_INVENTION_LIST_DRAG); - window_close_by_class(WC_EDITOR_INVENTION_LIST); - window_close_by_class(WC_EDITOR_OBJECT_SELECTION); - window_close_by_class(WC_EDITOR_OBJECTIVE_OPTIONS); - window_close_by_class(WC_EDITOR_SCENARIO_OPTIONS); - window_close_by_class(WC_FINANCES); - window_close_by_class(WC_FIRE_PROMPT); - window_close_by_class(WC_GUEST_LIST); - window_close_by_class(WC_INSTALL_TRACK); - window_close_by_class(WC_PEEP); - window_close_by_class(WC_RIDE); - window_close_by_class(WC_RIDE_CONSTRUCTION); - window_close_by_class(WC_RIDE_LIST); - window_close_by_class(WC_SCENERY); - window_close_by_class(WC_STAFF); - window_close_by_class(WC_TRACK_DELETE_PROMPT); - window_close_by_class(WC_TRACK_DESIGN_LIST); - window_close_by_class(WC_TRACK_DESIGN_PLACE); - } - - void PrepareParkForPlayback() - { - auto windowManager = GetContext()->GetUiContext()->GetWindowManager(); - windowManager->SetMainView(gSavedView, gSavedViewZoom, gSavedViewRotation); - ResetEntitySpatialIndices(); - reset_all_sprite_quadrant_placements(); - auto intent = Intent(INTENT_ACTION_REFRESH_NEW_RIDES); - context_broadcast_intent(&intent); - scenery_set_default_placement_configuration(); - News::InitQueue(); - load_palette(); - gScreenAge = 0; - gGamePaused = false; - gGameSpeed = 1; - } - - /** - * Sets the map location to the given (big) coordinates. Z is automatic. - * @param loc X and Y position in big coordinates. - */ - void SetViewLocation(const CoordsXY& loc) - { - // Update viewport - rct_window* w = window_get_main(); - if (w != nullptr) - { - int32_t z = tile_element_height(loc); - - // Prevent scroll adjustment due to window placement when in-game - auto oldScreenFlags = gScreenFlags; - gScreenFlags = SCREEN_FLAGS_TITLE_DEMO; - w->SetLocation({ loc, z }); - gScreenFlags = oldScreenFlags; - - viewport_update_position(w); - - // Save known tile position in case of window resize - _lastScreenWidth = w->width; - _lastScreenHeight = w->height; - _viewCentreLocation = loc; - } - } - - /** - * Fixes the view location for when the game window has changed size. - */ - void FixViewLocation() - { - rct_window* w = window_get_main(); - if (w != nullptr && w->viewport_smart_follow_sprite.IsNull()) - { - if (w->width != _lastScreenWidth || w->height != _lastScreenHeight) - { - SetViewLocation(_viewCentreLocation); + if (w->width != _lastScreenWidth || w->height != _lastScreenHeight) + { + // SetViewLocation(_viewCentreLocation); + } } } - } -}; + }; -std::unique_ptr CreateTitleSequencePlayer(GameState& gameState) -{ - return std::make_unique(gameState); -} + std::unique_ptr CreateTitleSequencePlayer(GameState& gameState) + { + return std::make_unique(gameState); + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2-ui/title/TitleSequencePlayer.h b/src/openrct2-ui/title/TitleSequencePlayer.h index 1d38e714fc..ed868235b3 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.h +++ b/src/openrct2-ui/title/TitleSequencePlayer.h @@ -18,6 +18,9 @@ struct IScenarioRepository; namespace OpenRCT2 { class GameState; -} -[[nodiscard]] std::unique_ptr CreateTitleSequencePlayer(OpenRCT2::GameState& gameState); + namespace Title + { + [[nodiscard]] std::unique_ptr CreateTitleSequencePlayer(GameState& gameState); + } // namespace Title +} // namespace OpenRCT2 diff --git a/src/openrct2/title/Command/LoadPark.cpp b/src/openrct2/title/Command/LoadPark.cpp index 27366fe160..ff83e2aa65 100644 --- a/src/openrct2/title/Command/LoadPark.cpp +++ b/src/openrct2/title/Command/LoadPark.cpp @@ -13,8 +13,7 @@ namespace OpenRCT2::Title { int16_t LoadParkCommand::operator()(int16_t timer) { - // TODO: Load park - + // Park loading is currently handled by the title sequence player return 0; } } // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/LoadScenario.cpp b/src/openrct2/title/Command/LoadScenario.cpp index cfa1d300b8..fb9789d541 100644 --- a/src/openrct2/title/Command/LoadScenario.cpp +++ b/src/openrct2/title/Command/LoadScenario.cpp @@ -13,8 +13,7 @@ namespace OpenRCT2::Title { int16_t LoadScenarioCommand::operator()(int16_t timer) { - // TODO: Load scenario - + // Scenario loading is currently handled by the title sequence player return 0; } } // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/RotateView.cpp b/src/openrct2/title/Command/RotateView.cpp index 3c19914c11..448e1455de 100644 --- a/src/openrct2/title/Command/RotateView.cpp +++ b/src/openrct2/title/Command/RotateView.cpp @@ -9,11 +9,20 @@ #include "RotateView.h" +#include "../../interface/Window.h" + namespace OpenRCT2::Title { int16_t RotateViewCommand::operator()(int16_t timer) { - // TODO: Rotate the view X times + rct_window* w = window_get_main(); + if (w != nullptr) + { + for (uint_fast8_t i = 0; i < Rotations; i++) + { + window_rotate_camera(w, 1); + } + } return 0; } diff --git a/src/openrct2/title/Command/SetLocation.cpp b/src/openrct2/title/Command/SetLocation.cpp index fa2700dcf7..c5548f453f 100644 --- a/src/openrct2/title/Command/SetLocation.cpp +++ b/src/openrct2/title/Command/SetLocation.cpp @@ -9,11 +9,29 @@ #include "SetLocation.h" +#include "../../OpenRCT2.h" +#include "../../interface/Window.h" +#include "../../interface/Window_internal.h" +#include "../../world/Map.h" + namespace OpenRCT2::Title { int16_t SetLocationCommand::operator()(int16_t timer) { - // TODO: Update view location + rct_window* w = window_get_main(); + if (w != nullptr) + { + auto loc = TileCoordsXY(Location.X, Location.Y).ToCoordsXY().ToTileCentre(); + int32_t z = tile_element_height(loc); + + // Prevent scroll adjustment due to window placement when in-game + auto oldScreenFlags = gScreenFlags; + gScreenFlags = SCREEN_FLAGS_TITLE_DEMO; + w->SetLocation({ loc, z }); + gScreenFlags = oldScreenFlags; + + viewport_update_position(w); + } return 0; } diff --git a/src/openrct2/title/Command/SetSpeed.cpp b/src/openrct2/title/Command/SetSpeed.cpp index 095eaadd04..40189530d0 100644 --- a/src/openrct2/title/Command/SetSpeed.cpp +++ b/src/openrct2/title/Command/SetSpeed.cpp @@ -9,11 +9,15 @@ #include "SetSpeed.h" +#include "../../Game.h" + +#include + namespace OpenRCT2::Title { int16_t SetSpeedCommand::operator()(int16_t timer) { - // TODO: Update current zoom level + gGameSpeed = std::clamp(Speed, 1, 4); return 0; } diff --git a/src/openrct2/title/Command/SetZoom.cpp b/src/openrct2/title/Command/SetZoom.cpp index 21f41d2710..8a56b760e6 100644 --- a/src/openrct2/title/Command/SetZoom.cpp +++ b/src/openrct2/title/Command/SetZoom.cpp @@ -9,11 +9,18 @@ #include "SetZoom.h" +#include "../../interface/Window.h" +#include "../../interface/ZoomLevel.h" + namespace OpenRCT2::Title { int16_t SetZoomCommand::operator()(int16_t timer) { - // TODO: Update current zoom level + rct_window* w = window_get_main(); + if (w != nullptr) + { + window_zoom_set(w, ZoomLevel{ static_cast(Zoom) }, false); + } return 0; } diff --git a/src/openrct2/title/Command/Wait.cpp b/src/openrct2/title/Command/Wait.cpp index 76d7448bd4..ef06225a5f 100644 --- a/src/openrct2/title/Command/Wait.cpp +++ b/src/openrct2/title/Command/Wait.cpp @@ -9,10 +9,15 @@ #include "Wait.h" +#include "../../Context.h" + +#include + namespace OpenRCT2::Title { int16_t WaitCommand::operator()(int16_t timer) { - return Milliseconds; + // Return number of game ticks this wait command lasts + return std::max(1, GAME_UPDATE_FPS * Milliseconds / 1000); } } // namespace OpenRCT2::Title diff --git a/src/openrct2/title/TitleSequence.cpp b/src/openrct2/title/TitleSequence.cpp index 93ca63fdde..4fe53732ca 100644 --- a/src/openrct2/title/TitleSequence.cpp +++ b/src/openrct2/title/TitleSequence.cpp @@ -28,560 +28,563 @@ #include #include -#include #include -#include +#include #include +#include #include #include -using namespace OpenRCT2::Title; - -static std::vector GetSaves(const std::string& path); -static std::vector GetSaves(IZipArchive* zip); -static std::vector LegacyScriptRead(const std::vector& script, std::vector saves); -static void LegacyScriptGetLine(OpenRCT2::IStream* stream, std::vector>& parts); -static std::vector ReadScriptFile(const std::string& path); -static std::string LegacyScriptWrite(const TitleSequence& seq); - -std::unique_ptr CreateTitleSequence() +namespace OpenRCT2::Title { - return std::make_unique(); -} + static std::vector GetSaves(const std::string& path); + static std::vector GetSaves(IZipArchive* zip); + static std::vector LegacyScriptRead(const std::vector& script, std::vector saves); + static void LegacyScriptGetLine(OpenRCT2::IStream* stream, std::vector>& parts); + static std::vector ReadScriptFile(const std::string& path); + static std::string LegacyScriptWrite(const TitleSequence& seq); -std::unique_ptr LoadTitleSequence(const std::string& path) -{ - std::vector script; - std::vector saves; - bool isZip; - - log_verbose("Loading title sequence: %s", path.c_str()); - - auto ext = Path::GetExtension(path); - if (String::Equals(ext, TITLE_SEQUENCE_EXTENSION)) + std::unique_ptr CreateTitleSequence() { - auto zip = Zip::TryOpen(path, ZIP_ACCESS::READ); - if (zip == nullptr) - { - Console::Error::WriteLine("Unable to open '%s'", path.c_str()); - return nullptr; - } - - script = zip->GetFileData("script.txt"); - if (script.empty()) - { - Console::Error::WriteLine("Unable to open script.txt in '%s'", path.c_str()); - return nullptr; - } - - saves = GetSaves(zip.get()); - isZip = true; - } - else - { - auto scriptPath = Path::Combine(path, u8"script.txt"); - script = ReadScriptFile(scriptPath); - if (script.empty()) - { - Console::Error::WriteLine("Unable to open '%s'", scriptPath.c_str()); - return nullptr; - } - - saves = GetSaves(path); - isZip = false; + return std::make_unique(); } - auto commands = LegacyScriptRead(script, saves); - - auto seq = OpenRCT2::Title::CreateTitleSequence(); - seq->Name = Path::GetFileNameWithoutExtension(path); - seq->Path = path; - seq->Saves = saves; - seq->Commands = commands; - seq->IsZip = isZip; - return seq; -} - -std::unique_ptr TitleSequenceGetParkHandle(const TitleSequence& seq, size_t index) -{ - std::unique_ptr handle; - if (index < seq.Saves.size()) + std::unique_ptr LoadTitleSequence(const std::string& path) { - const auto& filename = seq.Saves[index]; - if (seq.IsZip) + std::vector script; + std::vector saves; + bool isZip; + + log_verbose("Loading title sequence: %s", path.c_str()); + + auto ext = Path::GetExtension(path); + if (String::Equals(ext, TITLE_SEQUENCE_EXTENSION)) { - auto zip = Zip::TryOpen(seq.Path, ZIP_ACCESS::READ); - if (zip != nullptr) + auto zip = Zip::TryOpen(path, ZIP_ACCESS::READ); + if (zip == nullptr) { - auto data = zip->GetFileData(filename); - auto ms = std::make_unique(); - ms->Write(data.data(), data.size()); - ms->SetPosition(0); + Console::Error::WriteLine("Unable to open '%s'", path.c_str()); + return nullptr; + } - handle = std::make_unique(); - handle->Stream = std::move(ms); - handle->HintPath = filename; + script = zip->GetFileData("script.txt"); + if (script.empty()) + { + Console::Error::WriteLine("Unable to open script.txt in '%s'", path.c_str()); + return nullptr; + } + + saves = GetSaves(zip.get()); + isZip = true; + } + else + { + auto scriptPath = Path::Combine(path, u8"script.txt"); + script = ReadScriptFile(scriptPath); + if (script.empty()) + { + Console::Error::WriteLine("Unable to open '%s'", scriptPath.c_str()); + return nullptr; + } + + saves = GetSaves(path); + isZip = false; + } + + auto commands = LegacyScriptRead(script, saves); + + auto seq = OpenRCT2::Title::CreateTitleSequence(); + seq->Name = Path::GetFileNameWithoutExtension(path); + seq->Path = path; + seq->Saves = saves; + seq->Commands = commands; + seq->IsZip = isZip; + return seq; + } + + std::unique_ptr TitleSequenceGetParkHandle(const TitleSequence& seq, size_t index) + { + std::unique_ptr handle; + if (index < seq.Saves.size()) + { + const auto& filename = seq.Saves[index]; + if (seq.IsZip) + { + auto zip = Zip::TryOpen(seq.Path, ZIP_ACCESS::READ); + if (zip != nullptr) + { + auto data = zip->GetFileData(filename); + auto ms = std::make_unique(); + ms->Write(data.data(), data.size()); + ms->SetPosition(0); + + handle = std::make_unique(); + handle->Stream = std::move(ms); + handle->HintPath = filename; + } + else + { + Console::Error::WriteLine( + "Failed to open zipped path '%s' from zip '%s'", filename.c_str(), seq.Path.c_str()); + } } else { - Console::Error::WriteLine("Failed to open zipped path '%s' from zip '%s'", filename.c_str(), seq.Path.c_str()); + auto absolutePath = Path::Combine(seq.Path, filename); + std::unique_ptr fileStream = nullptr; + try + { + fileStream = std::make_unique(absolutePath, OpenRCT2::FILE_MODE_OPEN); + } + catch (const IOException& exception) + { + Console::Error::WriteLine(exception.what()); + } + + if (fileStream != nullptr) + { + handle = std::make_unique(); + handle->Stream = std::move(fileStream); + handle->HintPath = filename; + } } } - else - { - auto absolutePath = Path::Combine(seq.Path, filename); - std::unique_ptr fileStream = nullptr; - try - { - fileStream = std::make_unique(absolutePath, OpenRCT2::FILE_MODE_OPEN); - } - catch (const IOException& exception) - { - Console::Error::WriteLine(exception.what()); - } - - if (fileStream != nullptr) - { - handle = std::make_unique(); - handle->Stream = std::move(fileStream); - handle->HintPath = filename; - } - } - } - return handle; -} - -bool TitleSequenceSave(const TitleSequence& seq) -{ - try - { - auto script = LegacyScriptWrite(seq); - if (seq.IsZip) - { - auto fdata = std::vector(script.begin(), script.end()); - auto zip = Zip::Open(seq.Path, ZIP_ACCESS::WRITE); - zip->SetFileData("script.txt", std::move(fdata)); - } - else - { - auto scriptPath = Path::Combine(seq.Path, u8"script.txt"); - File::WriteAllBytes(scriptPath, script.data(), script.size()); - } - return true; - } - catch (const std::exception&) - { - return false; - } -} - -bool TitleSequenceAddPark(TitleSequence& seq, const utf8* path, const utf8* name) -{ - // Get new save index - auto it = std::find(seq.Saves.begin(), seq.Saves.end(), path); - if (it == seq.Saves.end()) - { - seq.Saves.push_back(name); + return handle; } - if (seq.IsZip) + bool TitleSequenceSave(const TitleSequence& seq) { try { - auto fdata = File::ReadAllBytes(path); + auto script = LegacyScriptWrite(seq); + if (seq.IsZip) + { + auto fdata = std::vector(script.begin(), script.end()); + auto zip = Zip::Open(seq.Path, ZIP_ACCESS::WRITE); + zip->SetFileData("script.txt", std::move(fdata)); + } + else + { + auto scriptPath = Path::Combine(seq.Path, u8"script.txt"); + File::WriteAllBytes(scriptPath, script.data(), script.size()); + } + return true; + } + catch (const std::exception&) + { + return false; + } + } + + bool TitleSequenceAddPark(TitleSequence& seq, const utf8* path, const utf8* name) + { + // Get new save index + auto it = std::find(seq.Saves.begin(), seq.Saves.end(), path); + if (it == seq.Saves.end()) + { + seq.Saves.push_back(name); + } + + if (seq.IsZip) + { + try + { + auto fdata = File::ReadAllBytes(path); + auto zip = Zip::TryOpen(seq.Path, ZIP_ACCESS::WRITE); + if (zip == nullptr) + { + Console::Error::WriteLine("Unable to open '%s'", seq.Path.c_str()); + return false; + } + zip->SetFileData(name, std::move(fdata)); + } + catch (const std::exception& ex) + { + Console::Error::WriteLine(ex.what()); + } + } + else + { + // Determine destination path + auto dstPath = Path::Combine(seq.Path, name); + if (!File::Copy(path, dstPath, true)) + { + Console::Error::WriteLine("Unable to copy '%s' to '%s'", path, dstPath.c_str()); + return false; + } + } + return true; + } + + bool TitleSequenceRenamePark(TitleSequence& seq, size_t index, const utf8* name) + { + Guard::Assert(index < seq.Saves.size(), GUARD_LINE); + + auto& oldRelativePath = seq.Saves[index]; + if (seq.IsZip) + { auto zip = Zip::TryOpen(seq.Path, ZIP_ACCESS::WRITE); if (zip == nullptr) { Console::Error::WriteLine("Unable to open '%s'", seq.Path.c_str()); return false; } - zip->SetFileData(name, std::move(fdata)); + zip->RenameFile(oldRelativePath, name); } - catch (const std::exception& ex) + else { - Console::Error::WriteLine(ex.what()); - } - } - else - { - // Determine destination path - auto dstPath = Path::Combine(seq.Path, name); - if (!File::Copy(path, dstPath, true)) - { - Console::Error::WriteLine("Unable to copy '%s' to '%s'", path, dstPath.c_str()); - return false; - } - } - return true; -} - -bool TitleSequenceRenamePark(TitleSequence& seq, size_t index, const utf8* name) -{ - Guard::Assert(index < seq.Saves.size(), GUARD_LINE); - - auto& oldRelativePath = seq.Saves[index]; - if (seq.IsZip) - { - auto zip = Zip::TryOpen(seq.Path, ZIP_ACCESS::WRITE); - if (zip == nullptr) - { - Console::Error::WriteLine("Unable to open '%s'", seq.Path.c_str()); - return false; - } - zip->RenameFile(oldRelativePath, name); - } - else - { - auto srcPath = Path::Combine(seq.Path, oldRelativePath); - auto dstPath = Path::Combine(seq.Path, name); - if (!File::Move(srcPath, dstPath)) - { - Console::Error::WriteLine("Unable to move '%s' to '%s'", srcPath.c_str(), dstPath.c_str()); - return false; - } - } - seq.Saves[index] = name; - return true; -} - -bool TitleSequenceRemovePark(TitleSequence& seq, size_t index) -{ - Guard::Assert(index < seq.Saves.size(), GUARD_LINE); - - // Delete park file - auto& relativePath = seq.Saves[index]; - if (seq.IsZip) - { - auto zip = Zip::TryOpen(seq.Path, ZIP_ACCESS::WRITE); - if (zip == nullptr) - { - Console::Error::WriteLine("Unable to open '%s'", seq.Path.c_str()); - return false; - } - zip->DeleteFile(relativePath); - } - else - { - auto absolutePath = Path::Combine(seq.Path, relativePath); - if (!File::Delete(absolutePath)) - { - Console::Error::WriteLine("Unable to delete '%s'", absolutePath.c_str()); - return false; + auto srcPath = Path::Combine(seq.Path, oldRelativePath); + auto dstPath = Path::Combine(seq.Path, name); + if (!File::Move(srcPath, dstPath)) + { + Console::Error::WriteLine("Unable to move '%s' to '%s'", srcPath.c_str(), dstPath.c_str()); + return false; + } } + seq.Saves[index] = name; + return true; } - // Remove from sequence - seq.Saves.erase(seq.Saves.begin() + index); - - // Update load commands - for (auto& command : seq.Commands) + bool TitleSequenceRemovePark(TitleSequence& seq, size_t index) { - std::visit( - [index](auto&& command) { - if constexpr (std::is_same_v, LoadParkCommand>) - { - if (command.SaveIndex == index) + Guard::Assert(index < seq.Saves.size(), GUARD_LINE); + + // Delete park file + auto& relativePath = seq.Saves[index]; + if (seq.IsZip) + { + auto zip = Zip::TryOpen(seq.Path, ZIP_ACCESS::WRITE); + if (zip == nullptr) + { + Console::Error::WriteLine("Unable to open '%s'", seq.Path.c_str()); + return false; + } + zip->DeleteFile(relativePath); + } + else + { + auto absolutePath = Path::Combine(seq.Path, relativePath); + if (!File::Delete(absolutePath)) + { + Console::Error::WriteLine("Unable to delete '%s'", absolutePath.c_str()); + return false; + } + } + + // Remove from sequence + seq.Saves.erase(seq.Saves.begin() + index); + + // Update load commands + for (auto& command : seq.Commands) + { + std::visit( + [index](auto&& command) { + if constexpr (std::is_same_v, LoadParkCommand>) { - // Park no longer exists, so reset load command to invalid - command.SaveIndex = SAVE_INDEX_INVALID; + if (command.SaveIndex == index) + { + // Park no longer exists, so reset load command to invalid + command.SaveIndex = SAVE_INDEX_INVALID; + } + else if (command.SaveIndex > index) + { + // Park index will have shifted by -1 + command.SaveIndex--; + } } - else if (command.SaveIndex > index) + }, + command); + } + + return true; + } + + static std::vector GetSaves(const std::string& directory) + { + std::vector saves; + + auto pattern = Path::Combine(directory, u8"*.sc6;*.sv6;*.park;*.sv4;*.sc4"); + auto scanner = Path::ScanDirectory(pattern, true); + while (scanner->Next()) + { + const utf8* path = scanner->GetPathRelative(); + saves.push_back(path); + } + return saves; + } + + static std::vector GetSaves(IZipArchive* zip) + { + std::vector saves; + size_t numFiles = zip->GetNumFiles(); + for (size_t i = 0; i < numFiles; i++) + { + auto name = zip->GetFileName(i); + auto ext = Path::GetExtension(name); + if (String::Equals(ext, ".sv6", true) || String::Equals(ext, ".sc6", true) || String::Equals(ext, ".park", true)) + { + saves.push_back(std::move(name)); + } + } + return saves; + } + + static std::vector LegacyScriptRead(const std::vector& script, std::vector saves) + { + std::vector commands; + auto fs = OpenRCT2::MemoryStream(script.data(), script.size()); + do + { + std::vector> parts; + LegacyScriptGetLine(&fs, parts); + + const char* token = parts[0].data(); + std::optional command = std::nullopt; + + if (token[0] != 0) + { + if (_stricmp(token, "LOAD") == 0) + { + auto saveIndex = SAVE_INDEX_INVALID; + const std::string relativePath = parts[1].data(); + for (size_t i = 0; i < saves.size(); i++) { - // Park index will have shifted by -1 - command.SaveIndex--; + if (String::Equals(relativePath, saves[i], true)) + { + saveIndex = static_cast(i); + break; + } } + command = LoadParkCommand{ saveIndex }; } - }, - command); + else if (_stricmp(token, "LOCATION") == 0) + { + uint8_t locationX = atoi(parts[1].data()) & 0xFF; + uint8_t locationY = atoi(parts[2].data()) & 0xFF; + command = SetLocationCommand{ locationX, locationY }; + } + else if (_stricmp(token, "ROTATE") == 0) + { + uint8_t rotations = atoi(parts[1].data()) & 0xFF; + command = RotateViewCommand{ rotations }; + } + else if (_stricmp(token, "ZOOM") == 0) + { + uint8_t zoom = atoi(parts[1].data()) & 0xFF; + command = SetZoomCommand{ zoom }; + } + else if (_stricmp(token, "SPEED") == 0) + { + uint8_t speed = std::max(1, std::min(4, atoi(parts[1].data()) & 0xFF)); + command = SetSpeedCommand{ speed }; + } + else if (_stricmp(token, "FOLLOW") == 0) + { + auto entityID = EntityId::FromUnderlying(atoi(parts[1].data()) & 0xFFFF); + auto followCommand = FollowEntityCommand{ entityID }; + safe_strcpy(followCommand.Follow.SpriteName, parts[2].data(), USER_STRING_MAX_LENGTH); + command = followCommand; + } + else if (_stricmp(token, "WAIT") == 0) + { + uint16_t milliseconds = atoi(parts[1].data()) & 0xFFFF; + command = WaitCommand{ milliseconds }; + } + else if (_stricmp(token, "RESTART") == 0) + { + command = RestartCommand{}; + } + else if (_stricmp(token, "END") == 0) + { + command = EndCommand{}; + } + else if (_stricmp(token, "LOADSC") == 0) + { + auto loadScenarioCommand = LoadScenarioCommand{}; + safe_strcpy(loadScenarioCommand.Scenario, parts[1].data(), sizeof(loadScenarioCommand.Scenario)); + command = loadScenarioCommand; + } + } + + if (command.has_value()) + { + commands.push_back(std::move(*command)); + } + } while (fs.GetPosition() < fs.GetLength()); + return commands; } - return true; -} - -static std::vector GetSaves(const std::string& directory) -{ - std::vector saves; - - auto pattern = Path::Combine(directory, u8"*.sc6;*.sv6;*.park;*.sv4;*.sc4"); - auto scanner = Path::ScanDirectory(pattern, true); - while (scanner->Next()) + static void LegacyScriptGetLine(OpenRCT2::IStream* stream, std::vector>& parts) { - const utf8* path = scanner->GetPathRelative(); - saves.push_back(path); - } - return saves; -} + int32_t part = 0; + int32_t cindex = 0; + int32_t whitespace = 1; + int32_t comment = 0; + bool load = false; + bool sprite = false; -static std::vector GetSaves(IZipArchive* zip) -{ - std::vector saves; - size_t numFiles = zip->GetNumFiles(); - for (size_t i = 0; i < numFiles; i++) - { - auto name = zip->GetFileName(i); - auto ext = Path::GetExtension(name); - if (String::Equals(ext, ".sv6", true) || String::Equals(ext, ".sc6", true) || String::Equals(ext, ".park", true)) - { - saves.push_back(std::move(name)); - } - } - return saves; -} + parts.resize(1); -static std::vector LegacyScriptRead(const std::vector& script, std::vector saves) -{ - std::vector commands; - auto fs = OpenRCT2::MemoryStream(script.data(), script.size()); - do - { - std::vector> parts; - LegacyScriptGetLine(&fs, parts); - - const char* token = parts[0].data(); - std::optional command = std::nullopt; - - if (token[0] != 0) + while (true) { - if (_stricmp(token, "LOAD") == 0) + int32_t c = 0; + if (stream->TryRead(&c, 1) != 1) { - auto saveIndex = SAVE_INDEX_INVALID; - for (size_t i = 0; i < saves.size(); i++) - { - if (String::Equals(parts[1].data(), saves[i], true)) - { - saveIndex = static_cast(i); - break; - } - } - command = LoadParkCommand{ saveIndex }; + c = EOF; } - else if (_stricmp(token, "LOCATION") == 0) - { - uint8_t locationX = atoi(parts[1].data()) & 0xFF; - uint8_t locationY = atoi(parts[2].data()) & 0xFF; - command = SetLocationCommand{ locationX, locationY }; - } - else if (_stricmp(token, "ROTATE") == 0) - { - uint8_t rotations = atoi(parts[1].data()) & 0xFF; - command = RotateViewCommand{ rotations }; - } - else if (_stricmp(token, "ZOOM") == 0) - { - uint8_t zoom = atoi(parts[1].data()) & 0xFF; - command = SetZoomCommand{ zoom }; - } - else if (_stricmp(token, "SPEED") == 0) - { - uint8_t speed = std::max(1, std::min(4, atoi(parts[1].data()) & 0xFF)); - command = SetSpeedCommand{ speed }; - } - else if (_stricmp(token, "FOLLOW") == 0) - { - auto entityID = EntityId::FromUnderlying(atoi(parts[1].data()) & 0xFFFF); - auto followCommand = FollowEntityCommand{ entityID }; - safe_strcpy(followCommand.Follow.SpriteName, parts[2].data(), USER_STRING_MAX_LENGTH); - command = followCommand; - } - else if (_stricmp(token, "WAIT") == 0) - { - uint16_t milliseconds = atoi(parts[1].data()) & 0xFFFF; - command = WaitCommand{ milliseconds }; - } - else if (_stricmp(token, "RESTART") == 0) - { - command = RestartCommand{}; - } - else if (_stricmp(token, "END") == 0) - { - command = EndCommand{}; - } - else if (_stricmp(token, "LOADSC") == 0) - { - auto loadScenarioCommand = LoadScenarioCommand{}; - safe_strcpy(loadScenarioCommand.Scenario, parts[1].data(), sizeof(loadScenarioCommand.Scenario)); - command = loadScenarioCommand; - } - } - - if (command.has_value()) - { - commands.push_back(std::move(*command)); - } - } while (fs.GetPosition() < fs.GetLength()); - return commands; -} - -static void LegacyScriptGetLine(OpenRCT2::IStream* stream, std::vector>& parts) -{ - int32_t part = 0; - int32_t cindex = 0; - int32_t whitespace = 1; - int32_t comment = 0; - bool load = false; - bool sprite = false; - - parts.resize(1); - - while (true) - { - int32_t c = 0; - if (stream->TryRead(&c, 1) != 1) - { - c = EOF; - } - if (c == '\n' || c == '\r' || c == EOF) - { - parts[part][cindex] = 0; - return; - } - if (c == '#') - { - parts[part][cindex] = 0; - comment = 1; - } - else if (c == ' ' && !comment && !load && (!sprite || part != 2)) - { - if (!whitespace) - { - if (part == 0 - && ((cindex == 4 && _strnicmp(parts[0].data(), "LOAD", 4) == 0) - || (cindex == 6 && _strnicmp(parts[0].data(), "LOADSC", 6) == 0))) - { - load = true; - } - else if (part == 0 && cindex == 6 && _strnicmp(parts[0].data(), "FOLLOW", 6) == 0) - { - sprite = true; - } - parts[part][cindex] = 0; - part++; - parts.resize(part + 1); - cindex = 0; - } - } - else if (!comment) - { - whitespace = 0; - if (cindex < 127) - { - parts[part][cindex] = c; - cindex++; - } - else + if (c == '\n' || c == '\r' || c == EOF) { parts[part][cindex] = 0; - part++; - parts.resize(part + 1); - cindex = 0; + return; + } + if (c == '#') + { + parts[part][cindex] = 0; + comment = 1; + } + else if (c == ' ' && !comment && !load && (!sprite || part != 2)) + { + if (!whitespace) + { + if (part == 0 + && ((cindex == 4 && _strnicmp(parts[0].data(), "LOAD", 4) == 0) + || (cindex == 6 && _strnicmp(parts[0].data(), "LOADSC", 6) == 0))) + { + load = true; + } + else if (part == 0 && cindex == 6 && _strnicmp(parts[0].data(), "FOLLOW", 6) == 0) + { + sprite = true; + } + parts[part][cindex] = 0; + part++; + parts.resize(part + 1); + cindex = 0; + } + } + else if (!comment) + { + whitespace = 0; + if (cindex < 127) + { + parts[part][cindex] = c; + cindex++; + } + else + { + parts[part][cindex] = 0; + part++; + parts.resize(part + 1); + cindex = 0; + } } } } -} -static std::vector ReadScriptFile(const std::string& path) -{ - std::vector result; - try + static std::vector ReadScriptFile(const std::string& path) { - auto fs = OpenRCT2::FileStream(path, OpenRCT2::FILE_MODE_OPEN); - auto size = static_cast(fs.GetLength()); - result.resize(size); - fs.Read(result.data(), size); + std::vector result; + try + { + auto fs = OpenRCT2::FileStream(path, OpenRCT2::FILE_MODE_OPEN); + auto size = static_cast(fs.GetLength()); + result.resize(size); + fs.Read(result.data(), size); + } + catch (const std::exception&) + { + result.clear(); + result.shrink_to_fit(); + } + return result; } - catch (const std::exception&) - { - result.clear(); - result.shrink_to_fit(); - } - return result; -} -static std::string LegacyScriptWrite(const TitleSequence& seq) -{ - utf8 buffer[128]; - auto sb = StringBuilder(128); - - sb.Append("# SCRIPT FOR "); - sb.Append(seq.Name.c_str()); - sb.Append("\n"); - for (const auto& command : seq.Commands) + static std::string LegacyScriptWrite(const TitleSequence& seq) { - std::visit( - [&buffer, &seq, &sb](auto&& command) { - using T = std::decay::type; - if constexpr (std::is_same_v) - { - if (command.SaveIndex < seq.Saves.size()) - { - sb.Append("LOAD "); - sb.Append(seq.Saves[command.SaveIndex].c_str()); - } - else - { - sb.Append("LOAD "); - } - } - else if constexpr (std::is_same_v) - { - if (command.Scenario[0] == '\0') - { - sb.Append("LOADSC "); - } - else - { - sb.Append("LOADSC "); - sb.Append(command.Scenario); - } - } - else if constexpr (std::is_same_v) - { - String::Format(buffer, sizeof(buffer), "LOCATION %u %u", command.Location.X, command.Location.Y); - sb.Append(buffer); - } - else if constexpr (std::is_same_v) - { - String::Format(buffer, sizeof(buffer), "ROTATE %u", command.Rotations); - sb.Append(buffer); - } - else if constexpr (std::is_same_v) - { - String::Format(buffer, sizeof(buffer), "ZOOM %u", command.Zoom); - sb.Append(buffer); - } - else if constexpr (std::is_same_v) - { - String::Format(buffer, sizeof(buffer), "FOLLOW %u ", command.Follow.SpriteIndex); - sb.Append(buffer); - sb.Append(command.Follow.SpriteName); - } - else if constexpr (std::is_same_v) - { - String::Format(buffer, sizeof(buffer), "SPEED %u", command.Speed); - sb.Append(buffer); - } - else if constexpr (std::is_same_v) - { - String::Format(buffer, sizeof(buffer), "WAIT %u", command.Milliseconds); - sb.Append(buffer); - } - else if constexpr (std::is_same_v) - { - sb.Append("RESTART"); - } - else if constexpr (std::is_same_v) - { - sb.Append("END"); - } - }, - command); + utf8 buffer[128]; + auto sb = StringBuilder(128); + + sb.Append("# SCRIPT FOR "); + sb.Append(seq.Name.c_str()); sb.Append("\n"); + for (const auto& command : seq.Commands) + { + std::visit( + [&buffer, &seq, &sb](auto&& command) { + using T = std::decay::type; + if constexpr (std::is_same_v) + { + if (command.SaveIndex < seq.Saves.size()) + { + sb.Append("LOAD "); + sb.Append(seq.Saves[command.SaveIndex].c_str()); + } + else + { + sb.Append("LOAD "); + } + } + else if constexpr (std::is_same_v) + { + if (command.Scenario[0] == '\0') + { + sb.Append("LOADSC "); + } + else + { + sb.Append("LOADSC "); + sb.Append(command.Scenario); + } + } + else if constexpr (std::is_same_v) + { + String::Format(buffer, sizeof(buffer), "LOCATION %u %u", command.Location.X, command.Location.Y); + sb.Append(buffer); + } + else if constexpr (std::is_same_v) + { + String::Format(buffer, sizeof(buffer), "ROTATE %u", command.Rotations); + sb.Append(buffer); + } + else if constexpr (std::is_same_v) + { + String::Format(buffer, sizeof(buffer), "ZOOM %u", command.Zoom); + sb.Append(buffer); + } + else if constexpr (std::is_same_v) + { + String::Format(buffer, sizeof(buffer), "FOLLOW %u ", command.Follow.SpriteIndex); + sb.Append(buffer); + sb.Append(command.Follow.SpriteName); + } + else if constexpr (std::is_same_v) + { + String::Format(buffer, sizeof(buffer), "SPEED %u", command.Speed); + sb.Append(buffer); + } + else if constexpr (std::is_same_v) + { + String::Format(buffer, sizeof(buffer), "WAIT %u", command.Milliseconds); + sb.Append(buffer); + } + else if constexpr (std::is_same_v) + { + sb.Append("RESTART"); + } + else if constexpr (std::is_same_v) + { + sb.Append("END"); + } + }, + command); + sb.Append("\n"); + } + + return sb.GetBuffer(); } - return sb.GetBuffer(); -} - -bool TitleSequenceIsLoadCommand(const TitleCommand& command) -{ - return std::holds_alternative(command) || std::holds_alternative(command); -} + bool TitleSequenceIsLoadCommand(const TitleCommand& command) + { + return std::holds_alternative(command) || std::holds_alternative(command); + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/TitleSequenceManager.cpp b/src/openrct2/title/TitleSequenceManager.cpp index f80eaf04ab..ce8033b052 100644 --- a/src/openrct2/title/TitleSequenceManager.cpp +++ b/src/openrct2/title/TitleSequenceManager.cpp @@ -103,7 +103,7 @@ namespace TitleSequenceManager auto newPath = Path::Combine(Path::GetDirectory(oldPath), newName); if (item->IsZip) { - newPath += TITLE_SEQUENCE_EXTENSION; + newPath += OpenRCT2::Title::TITLE_SEQUENCE_EXTENSION; File::Move(oldPath, newPath); } else @@ -138,7 +138,7 @@ namespace TitleSequenceManager size_t CreateItem(const utf8* name) { - auto seq = CreateTitleSequence(); + auto seq = OpenRCT2::Title::CreateTitleSequence(); seq->Name = name; seq->Path = GetNewTitleSequencePath(seq->Name, true); seq->IsZip = true; @@ -159,7 +159,7 @@ namespace TitleSequenceManager auto path = Path::Combine(GetUserSequencesPath(), name); if (isZip) { - path += TITLE_SEQUENCE_EXTENSION; + path += OpenRCT2::Title::TITLE_SEQUENCE_EXTENSION; } return path; }