From debdb0bcda3333ef8c41f9cacf182c38ffa35b13 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Fri, 17 Jun 2022 23:34:37 +0200 Subject: [PATCH 01/12] Make title sequence commands list variadic --- src/openrct2/title/TitleSequence.cpp | 208 ++++++++++++++------------- src/openrct2/title/TitleSequence.h | 95 +++++++----- 2 files changed, 165 insertions(+), 138 deletions(-) diff --git a/src/openrct2/title/TitleSequence.cpp b/src/openrct2/title/TitleSequence.cpp index b828387199..16739a1a3f 100644 --- a/src/openrct2/title/TitleSequence.cpp +++ b/src/openrct2/title/TitleSequence.cpp @@ -28,7 +28,11 @@ #include #include +#include #include +#include +#include +#include #include static std::vector GetSaves(const std::string& path); @@ -270,19 +274,23 @@ bool TitleSequenceRemovePark(TitleSequence& seq, size_t index) // Update load commands for (auto& command : seq.Commands) { - if (command.Type == TitleScript::Load) - { - 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--; - } - } + std::visit( + [index](auto&& command) { + if constexpr (std::is_same_v, LoadParkCommand>) + { + 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--; + } + } + }, + command); } return true; @@ -328,73 +336,75 @@ static std::vector LegacyScriptRead(const std::vector& sc LegacyScriptGetLine(&fs, parts); const char* token = parts[0].data(); - TitleCommand command = {}; - command.Type = TitleScript::Undefined; + std::optional command = std::nullopt; if (token[0] != 0) { if (_stricmp(token, "LOAD") == 0) { - command.Type = TitleScript::Load; - command.SaveIndex = SAVE_INDEX_INVALID; + auto saveIndex = SAVE_INDEX_INVALID; for (size_t i = 0; i < saves.size(); i++) { if (String::Equals(parts[1].data(), saves[i], true)) { - command.SaveIndex = static_cast(i); + saveIndex = static_cast(i); break; } } + command = LoadParkCommand{ saveIndex }; } else if (_stricmp(token, "LOCATION") == 0) { - command.Type = TitleScript::Location; - command.Location.X = atoi(parts[1].data()) & 0xFF; - command.Location.Y = atoi(parts[2].data()) & 0xFF; + 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) { - command.Type = TitleScript::Rotate; - command.Rotations = atoi(parts[1].data()) & 0xFF; + uint8_t rotations = atoi(parts[1].data()) & 0xFF; + command = RotateViewCommand{ rotations }; } else if (_stricmp(token, "ZOOM") == 0) { - command.Type = TitleScript::Zoom; - command.Zoom = atoi(parts[1].data()) & 0xFF; + uint8_t zoom = atoi(parts[1].data()) & 0xFF; + command = SetZoomCommand{ zoom }; } else if (_stricmp(token, "SPEED") == 0) { - command.Type = TitleScript::Speed; - command.Speed = std::max(1, std::min(4, atoi(parts[1].data()) & 0xFF)); + uint8_t speed = std::max(1, std::min(4, atoi(parts[1].data()) & 0xFF)); + command = SetSpeedCommand{ speed }; } else if (_stricmp(token, "FOLLOW") == 0) { - command.Type = TitleScript::Follow; - command.Follow.SpriteIndex = EntityId::FromUnderlying(atoi(parts[1].data()) & 0xFFFF); - safe_strcpy(command.Follow.SpriteName, parts[2].data(), USER_STRING_MAX_LENGTH); + 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) { - command.Type = TitleScript::Wait; - command.Milliseconds = atoi(parts[1].data()) & 0xFFFF; + uint16_t milliseconds = atoi(parts[1].data()) & 0xFFFF; + command = WaitCommand{ milliseconds }; } else if (_stricmp(token, "RESTART") == 0) { - command.Type = TitleScript::Restart; + command = RestartCommand{}; } else if (_stricmp(token, "END") == 0) { - command.Type = TitleScript::End; + command = EndCommand{}; } else if (_stricmp(token, "LOADSC") == 0) { - command.Type = TitleScript::LoadSc; - safe_strcpy(command.Scenario, parts[1].data(), sizeof(command.Scenario)); + auto loadScenarioCommand = LoadScenarioCommand{}; + safe_strcpy(loadScenarioCommand.Scenario, parts[1].data(), sizeof(loadScenarioCommand.Scenario)); + command = loadScenarioCommand; } } - if (command.Type != TitleScript::Undefined) + + if (command.has_value()) { - commands.push_back(std::move(command)); + commands.push_back(std::move(*command)); } } while (fs.GetPosition() < fs.GetLength()); return commands; @@ -495,67 +505,74 @@ static std::string LegacyScriptWrite(const TitleSequence& seq) sb.Append("\n"); for (const auto& command : seq.Commands) { - switch (command.Type) - { - case TitleScript::Load: - if (command.SaveIndex < seq.Saves.size()) + std::visit( + [&buffer, &seq, &sb](auto&& command) { + using T = std::decay::type; + if constexpr (std::is_same_v) { - sb.Append("LOAD "); - sb.Append(seq.Saves[command.SaveIndex].c_str()); + if (command.SaveIndex < seq.Saves.size()) + { + sb.Append("LOAD "); + sb.Append(seq.Saves[command.SaveIndex].c_str()); + } + else + { + sb.Append("LOAD "); + } } - else + else if constexpr (std::is_same_v) { - sb.Append("LOAD "); + if (command.Scenario[0] == '\0') + { + sb.Append("LOADSC "); + } + else + { + sb.Append("LOADSC "); + sb.Append(command.Scenario); + } } - break; - case TitleScript::LoadSc: - if (command.Scenario[0] == '\0') + else if constexpr (std::is_same_v) { - sb.Append("LOADSC "); + String::Format(buffer, sizeof(buffer), "LOCATION %u %u", command.Location.X, command.Location.Y); + sb.Append(buffer); } - else + else if constexpr (std::is_same_v) { - sb.Append("LOADSC "); - sb.Append(command.Scenario); + String::Format(buffer, sizeof(buffer), "ROTATE %u", command.Rotations); + sb.Append(buffer); } - break; - case TitleScript::Undefined: - break; - case TitleScript::Loop: - break; - case TitleScript::EndLoop: - break; - case TitleScript::Location: - String::Format(buffer, sizeof(buffer), "LOCATION %u %u", command.Location.X, command.Location.Y); - sb.Append(buffer); - break; - case TitleScript::Rotate: - String::Format(buffer, sizeof(buffer), "ROTATE %u", command.Rotations); - sb.Append(buffer); - break; - case TitleScript::Zoom: - String::Format(buffer, sizeof(buffer), "ZOOM %u", command.Zoom); - sb.Append(buffer); - break; - case TitleScript::Follow: - String::Format(buffer, sizeof(buffer), "FOLLOW %u ", command.Follow.SpriteIndex); - sb.Append(buffer); - sb.Append(command.Follow.SpriteName); - break; - case TitleScript::Speed: - String::Format(buffer, sizeof(buffer), "SPEED %u", command.Speed); - sb.Append(buffer); - break; - case TitleScript::Wait: - String::Format(buffer, sizeof(buffer), "WAIT %u", command.Milliseconds); - sb.Append(buffer); - break; - case TitleScript::Restart: - sb.Append("RESTART"); - break; - case TitleScript::End: - sb.Append("END"); - } + 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"); } @@ -564,12 +581,5 @@ static std::string LegacyScriptWrite(const TitleSequence& seq) bool TitleSequenceIsLoadCommand(const TitleCommand& command) { - switch (command.Type) - { - case TitleScript::Load: - case TitleScript::LoadSc: - return true; - default: - return false; - } + return std::holds_alternative(command) || std::holds_alternative(command); } diff --git a/src/openrct2/title/TitleSequence.h b/src/openrct2/title/TitleSequence.h index 9dfe3d707e..d1771548b8 100644 --- a/src/openrct2/title/TitleSequence.h +++ b/src/openrct2/title/TitleSequence.h @@ -14,32 +14,66 @@ #include "../openrct2/core/IStream.hpp" #include +#include #define TITLE_COMMAND_SCENARIO_LENGTH 64 -enum class TitleScript : uint8_t; -struct TitleCommand + +struct WaitCommand { - TitleScript Type; - union - { - uint8_t SaveIndex; // LOAD (this index is internal only) - struct // LOCATION - { - uint8_t X; - uint8_t Y; - } Location; - uint8_t Rotations; // ROTATE (counter-clockwise) - uint8_t Zoom; // ZOOM - struct // FOLLOW - { - EntityId SpriteIndex; - utf8 SpriteName[USER_STRING_MAX_LENGTH]; - } Follow; - uint8_t Speed; // SPEED - uint16_t Milliseconds; // WAIT - utf8 Scenario[TITLE_COMMAND_SCENARIO_LENGTH]; // LOADSC - }; + uint16_t Milliseconds; }; +struct SetLocationCommand +{ + struct + { + uint8_t X; + uint8_t Y; + } Location; +}; +struct RotateViewCommand +{ + uint8_t Rotations; +}; +struct SetZoomCommand +{ + uint8_t Zoom; +}; +struct FollowEntityCommand +{ + struct + { + EntityId SpriteIndex; + utf8 SpriteName[USER_STRING_MAX_LENGTH]; + } Follow; +}; +struct RestartCommand +{ +}; +struct LoadParkCommand +{ + uint8_t SaveIndex; +}; +struct EndCommand +{ +}; +struct SetSpeedCommand +{ + uint8_t Speed; +}; +struct LoopCommand +{ +}; +struct EndLoopCommand +{ +}; +struct LoadScenarioCommand +{ + utf8 Scenario[TITLE_COMMAND_SCENARIO_LENGTH]; +}; + +using TitleCommand = std::variant< + WaitCommand, SetLocationCommand, RotateViewCommand, SetZoomCommand, FollowEntityCommand, RestartCommand, LoadParkCommand, + EndCommand, SetSpeedCommand, LoopCommand, EndLoopCommand, LoadScenarioCommand>; struct TitleSequence { @@ -58,23 +92,6 @@ struct TitleSequenceParkHandle std::unique_ptr Stream; }; -enum class TitleScript : uint8_t -{ - Undefined = 0xFF, - Wait = 0, - Location, - Rotate, - Zoom, - Follow, - Restart, - Load, - End, - Speed, - Loop, - EndLoop, - LoadSc, -}; - constexpr const utf8* TITLE_SEQUENCE_EXTENSION = ".parkseq"; constexpr uint8_t SAVE_INDEX_INVALID = UINT8_MAX; From 6bfba888458d29403edf95ac90c96512965bf3ee Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Sat, 18 Jun 2022 17:21:33 +0200 Subject: [PATCH 02/12] Move title commands to their own source files --- src/openrct2/interface/Window.h | 1 - src/openrct2/libopenrct2.vcxproj | 22 +++- src/openrct2/title/Command/End.cpp | 18 +++ src/openrct2/title/Command/End.h | 22 ++++ src/openrct2/title/Command/FollowEntity.cpp | 20 ++++ src/openrct2/title/Command/FollowEntity.h | 32 +++++ src/openrct2/title/Command/LoadPark.cpp | 20 ++++ src/openrct2/title/Command/LoadPark.h | 24 ++++ src/openrct2/title/Command/LoadScenario.cpp | 20 ++++ src/openrct2/title/Command/LoadScenario.h | 28 +++++ src/openrct2/title/Command/Restart.cpp | 18 +++ src/openrct2/title/Command/Restart.h | 22 ++++ src/openrct2/title/Command/RotateView.cpp | 20 ++++ src/openrct2/title/Command/RotateView.h | 24 ++++ src/openrct2/title/Command/SetLocation.cpp | 20 ++++ src/openrct2/title/Command/SetLocation.h | 29 +++++ src/openrct2/title/Command/SetSpeed.cpp | 20 ++++ src/openrct2/title/Command/SetSpeed.h | 24 ++++ src/openrct2/title/Command/SetZoom.cpp | 20 ++++ src/openrct2/title/Command/SetZoom.h | 25 ++++ src/openrct2/title/Command/Wait.cpp | 18 +++ src/openrct2/title/Command/Wait.h | 24 ++++ src/openrct2/title/TitleSequence.cpp | 4 +- src/openrct2/title/TitleSequence.h | 124 +++++++------------- 24 files changed, 513 insertions(+), 86 deletions(-) create mode 100644 src/openrct2/title/Command/End.cpp create mode 100644 src/openrct2/title/Command/End.h create mode 100644 src/openrct2/title/Command/FollowEntity.cpp create mode 100644 src/openrct2/title/Command/FollowEntity.h create mode 100644 src/openrct2/title/Command/LoadPark.cpp create mode 100644 src/openrct2/title/Command/LoadPark.h create mode 100644 src/openrct2/title/Command/LoadScenario.cpp create mode 100644 src/openrct2/title/Command/LoadScenario.h create mode 100644 src/openrct2/title/Command/Restart.cpp create mode 100644 src/openrct2/title/Command/Restart.h create mode 100644 src/openrct2/title/Command/RotateView.cpp create mode 100644 src/openrct2/title/Command/RotateView.h create mode 100644 src/openrct2/title/Command/SetLocation.cpp create mode 100644 src/openrct2/title/Command/SetLocation.h create mode 100644 src/openrct2/title/Command/SetSpeed.cpp create mode 100644 src/openrct2/title/Command/SetSpeed.h create mode 100644 src/openrct2/title/Command/SetZoom.cpp create mode 100644 src/openrct2/title/Command/SetZoom.h create mode 100644 src/openrct2/title/Command/Wait.cpp create mode 100644 src/openrct2/title/Command/Wait.h diff --git a/src/openrct2/interface/Window.h b/src/openrct2/interface/Window.h index ed42c0ab67..488ba30c0e 100644 --- a/src/openrct2/interface/Window.h +++ b/src/openrct2/interface/Window.h @@ -27,7 +27,6 @@ struct rct_drawpixelinfo; struct rct_window; union rct_window_event; struct track_design_file_ref; -struct TitleSequence; struct TextInputSession; struct scenario_index_entry; diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 0249cf33b8..4b3cbcb56f 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -497,6 +497,16 @@ + + + + + + + + + + @@ -941,6 +951,16 @@ + + + + + + + + + + @@ -975,4 +995,4 @@ - + \ No newline at end of file diff --git a/src/openrct2/title/Command/End.cpp b/src/openrct2/title/Command/End.cpp new file mode 100644 index 0000000000..2249a42b1d --- /dev/null +++ b/src/openrct2/title/Command/End.cpp @@ -0,0 +1,18 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "End.h" + +namespace OpenRCT2::Title +{ + int16_t EndCommand::operator()(int16_t timer) + { + return 0; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/End.h b/src/openrct2/title/Command/End.h new file mode 100644 index 0000000000..f6bda4a516 --- /dev/null +++ b/src/openrct2/title/Command/End.h @@ -0,0 +1,22 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include + +namespace OpenRCT2::Title +{ + struct EndCommand + { + static constexpr const char* Name = "End Command"; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/FollowEntity.cpp b/src/openrct2/title/Command/FollowEntity.cpp new file mode 100644 index 0000000000..5db67999a9 --- /dev/null +++ b/src/openrct2/title/Command/FollowEntity.cpp @@ -0,0 +1,20 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "FollowEntity.h" + +namespace OpenRCT2::Title +{ + int16_t FollowEntityCommand::operator()(int16_t timer) + { + // TODO: Set entity to follow + + return 0; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/FollowEntity.h b/src/openrct2/title/Command/FollowEntity.h new file mode 100644 index 0000000000..9224eb3a90 --- /dev/null +++ b/src/openrct2/title/Command/FollowEntity.h @@ -0,0 +1,32 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include "../../Identifiers.h" +#include "../../core/String.hpp" +#include "../../localisation/Localisation.h" + +#include + +namespace OpenRCT2::Title +{ + struct FollowEntityCommand + { + static constexpr const char* Name = "Follow Entity Command"; + + struct + { + EntityId SpriteIndex; + utf8 SpriteName[USER_STRING_MAX_LENGTH]; + } Follow; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/LoadPark.cpp b/src/openrct2/title/Command/LoadPark.cpp new file mode 100644 index 0000000000..27366fe160 --- /dev/null +++ b/src/openrct2/title/Command/LoadPark.cpp @@ -0,0 +1,20 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "LoadPark.h" + +namespace OpenRCT2::Title +{ + int16_t LoadParkCommand::operator()(int16_t timer) + { + // TODO: Load park + + return 0; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/LoadPark.h b/src/openrct2/title/Command/LoadPark.h new file mode 100644 index 0000000000..e2569a0c2b --- /dev/null +++ b/src/openrct2/title/Command/LoadPark.h @@ -0,0 +1,24 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include + +namespace OpenRCT2::Title +{ + struct LoadParkCommand + { + static constexpr const char* Name = "Load Park Command"; + + uint8_t SaveIndex; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/LoadScenario.cpp b/src/openrct2/title/Command/LoadScenario.cpp new file mode 100644 index 0000000000..cfa1d300b8 --- /dev/null +++ b/src/openrct2/title/Command/LoadScenario.cpp @@ -0,0 +1,20 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "LoadScenario.h" + +namespace OpenRCT2::Title +{ + int16_t LoadScenarioCommand::operator()(int16_t timer) + { + // TODO: Load scenario + + return 0; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/LoadScenario.h b/src/openrct2/title/Command/LoadScenario.h new file mode 100644 index 0000000000..b27bbbafe5 --- /dev/null +++ b/src/openrct2/title/Command/LoadScenario.h @@ -0,0 +1,28 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include "../../core/String.hpp" + +#include + +#define TITLE_COMMAND_SCENARIO_LENGTH 64 + +namespace OpenRCT2::Title +{ + struct LoadScenarioCommand + { + static constexpr const char* Name = "Load Scenario Command"; + + utf8 Scenario[TITLE_COMMAND_SCENARIO_LENGTH]; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/Restart.cpp b/src/openrct2/title/Command/Restart.cpp new file mode 100644 index 0000000000..07dd2b7204 --- /dev/null +++ b/src/openrct2/title/Command/Restart.cpp @@ -0,0 +1,18 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "Restart.h" + +namespace OpenRCT2::Title +{ + int16_t RestartCommand::operator()(int16_t timer) + { + return 0; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/Restart.h b/src/openrct2/title/Command/Restart.h new file mode 100644 index 0000000000..95e2494c6d --- /dev/null +++ b/src/openrct2/title/Command/Restart.h @@ -0,0 +1,22 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include + +namespace OpenRCT2::Title +{ + struct RestartCommand + { + static constexpr const char* Name = "Restart Command"; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/RotateView.cpp b/src/openrct2/title/Command/RotateView.cpp new file mode 100644 index 0000000000..3c19914c11 --- /dev/null +++ b/src/openrct2/title/Command/RotateView.cpp @@ -0,0 +1,20 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "RotateView.h" + +namespace OpenRCT2::Title +{ + int16_t RotateViewCommand::operator()(int16_t timer) + { + // TODO: Rotate the view X times + + return 0; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/RotateView.h b/src/openrct2/title/Command/RotateView.h new file mode 100644 index 0000000000..91c52f46e2 --- /dev/null +++ b/src/openrct2/title/Command/RotateView.h @@ -0,0 +1,24 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include + +namespace OpenRCT2::Title +{ + struct RotateViewCommand + { + static constexpr const char* Name = "Rotate View Command"; + + uint8_t Rotations; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/SetLocation.cpp b/src/openrct2/title/Command/SetLocation.cpp new file mode 100644 index 0000000000..fa2700dcf7 --- /dev/null +++ b/src/openrct2/title/Command/SetLocation.cpp @@ -0,0 +1,20 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "SetLocation.h" + +namespace OpenRCT2::Title +{ + int16_t SetLocationCommand::operator()(int16_t timer) + { + // TODO: Update view location + + return 0; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/SetLocation.h b/src/openrct2/title/Command/SetLocation.h new file mode 100644 index 0000000000..fef8b52db2 --- /dev/null +++ b/src/openrct2/title/Command/SetLocation.h @@ -0,0 +1,29 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include + +namespace OpenRCT2::Title +{ + struct SetLocationCommand + { + static constexpr const char* Name = "Set Location Command"; + + // TODO: Use TileCoordsXY instead + struct + { + uint8_t X; + uint8_t Y; + } Location; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/SetSpeed.cpp b/src/openrct2/title/Command/SetSpeed.cpp new file mode 100644 index 0000000000..095eaadd04 --- /dev/null +++ b/src/openrct2/title/Command/SetSpeed.cpp @@ -0,0 +1,20 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "SetSpeed.h" + +namespace OpenRCT2::Title +{ + int16_t SetSpeedCommand::operator()(int16_t timer) + { + // TODO: Update current zoom level + + return 0; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/SetSpeed.h b/src/openrct2/title/Command/SetSpeed.h new file mode 100644 index 0000000000..96c97a32b1 --- /dev/null +++ b/src/openrct2/title/Command/SetSpeed.h @@ -0,0 +1,24 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include + +namespace OpenRCT2::Title +{ + struct SetSpeedCommand + { + static constexpr const char* Name = "Set Speed Command"; + + uint8_t Speed; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/SetZoom.cpp b/src/openrct2/title/Command/SetZoom.cpp new file mode 100644 index 0000000000..21f41d2710 --- /dev/null +++ b/src/openrct2/title/Command/SetZoom.cpp @@ -0,0 +1,20 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "SetZoom.h" + +namespace OpenRCT2::Title +{ + int16_t SetZoomCommand::operator()(int16_t timer) + { + // TODO: Update current zoom level + + return 0; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/SetZoom.h b/src/openrct2/title/Command/SetZoom.h new file mode 100644 index 0000000000..8c78f3fdde --- /dev/null +++ b/src/openrct2/title/Command/SetZoom.h @@ -0,0 +1,25 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include + +namespace OpenRCT2::Title +{ + struct SetZoomCommand + { + static constexpr const char* Name = "Set Zoom Command"; + + // TODO: Use ZoomLevel instead + uint8_t Zoom; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/Wait.cpp b/src/openrct2/title/Command/Wait.cpp new file mode 100644 index 0000000000..76d7448bd4 --- /dev/null +++ b/src/openrct2/title/Command/Wait.cpp @@ -0,0 +1,18 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#include "Wait.h" + +namespace OpenRCT2::Title +{ + int16_t WaitCommand::operator()(int16_t timer) + { + return Milliseconds; + } +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/Wait.h b/src/openrct2/title/Command/Wait.h new file mode 100644 index 0000000000..c14317c9d3 --- /dev/null +++ b/src/openrct2/title/Command/Wait.h @@ -0,0 +1,24 @@ +/***************************************************************************** + * Copyright (c) 2014-2022 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. + *****************************************************************************/ + +#pragma once + +#include + +namespace OpenRCT2::Title +{ + struct WaitCommand + { + static constexpr const char* Name = "Wait Command"; + + uint16_t Milliseconds; + + int16_t operator()(int16_t timer); + }; +} // namespace OpenRCT2::Title diff --git a/src/openrct2/title/TitleSequence.cpp b/src/openrct2/title/TitleSequence.cpp index 16739a1a3f..93ca63fdde 100644 --- a/src/openrct2/title/TitleSequence.cpp +++ b/src/openrct2/title/TitleSequence.cpp @@ -35,6 +35,8 @@ #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); @@ -91,7 +93,7 @@ std::unique_ptr LoadTitleSequence(const std::string& path) auto commands = LegacyScriptRead(script, saves); - auto seq = CreateTitleSequence(); + auto seq = OpenRCT2::Title::CreateTitleSequence(); seq->Name = Path::GetFileNameWithoutExtension(path); seq->Path = path; seq->Saves = saves; diff --git a/src/openrct2/title/TitleSequence.h b/src/openrct2/title/TitleSequence.h index d1771548b8..af5c08fc6f 100644 --- a/src/openrct2/title/TitleSequence.h +++ b/src/openrct2/title/TitleSequence.h @@ -10,98 +10,56 @@ #pragma once #include "../common.h" -#include "../localisation/Localisation.h" #include "../openrct2/core/IStream.hpp" +#include "Command/End.h" +#include "Command/FollowEntity.h" +#include "Command/LoadPark.h" +#include "Command/LoadScenario.h" +#include "Command/Restart.h" +#include "Command/RotateView.h" +#include "Command/SetLocation.h" +#include "Command/SetSpeed.h" +#include "Command/SetZoom.h" +#include "Command/Wait.h" #include +#include #include -#define TITLE_COMMAND_SCENARIO_LENGTH 64 +namespace OpenRCT2::Title +{ + using TitleCommand = std::variant< + WaitCommand, SetLocationCommand, RotateViewCommand, SetZoomCommand, FollowEntityCommand, RestartCommand, + LoadParkCommand, EndCommand, SetSpeedCommand, LoadScenarioCommand>; -struct WaitCommand -{ - uint16_t Milliseconds; -}; -struct SetLocationCommand -{ - struct + struct TitleSequence { - uint8_t X; - uint8_t Y; - } Location; -}; -struct RotateViewCommand -{ - uint8_t Rotations; -}; -struct SetZoomCommand -{ - uint8_t Zoom; -}; -struct FollowEntityCommand -{ - struct + std::string Name; + std::string Path; + + std::vector Commands; + std::vector Saves; + + bool IsZip = false; + }; + + struct TitleSequenceParkHandle { - EntityId SpriteIndex; - utf8 SpriteName[USER_STRING_MAX_LENGTH]; - } Follow; -}; -struct RestartCommand -{ -}; -struct LoadParkCommand -{ - uint8_t SaveIndex; -}; -struct EndCommand -{ -}; -struct SetSpeedCommand -{ - uint8_t Speed; -}; -struct LoopCommand -{ -}; -struct EndLoopCommand -{ -}; -struct LoadScenarioCommand -{ - utf8 Scenario[TITLE_COMMAND_SCENARIO_LENGTH]; -}; + std::string HintPath; + std::unique_ptr Stream; + }; -using TitleCommand = std::variant< - WaitCommand, SetLocationCommand, RotateViewCommand, SetZoomCommand, FollowEntityCommand, RestartCommand, LoadParkCommand, - EndCommand, SetSpeedCommand, LoopCommand, EndLoopCommand, LoadScenarioCommand>; + constexpr const utf8* TITLE_SEQUENCE_EXTENSION = ".parkseq"; + constexpr uint8_t SAVE_INDEX_INVALID = UINT8_MAX; -struct TitleSequence -{ - std::string Name; - std::string Path; + [[nodiscard]] std::unique_ptr CreateTitleSequence(); + [[nodiscard]] std::unique_ptr LoadTitleSequence(const std::string& path); + [[nodiscard]] std::unique_ptr TitleSequenceGetParkHandle(const TitleSequence& seq, size_t index); - std::vector Commands; - std::vector Saves; + bool TitleSequenceSave(const TitleSequence& seq); + bool TitleSequenceAddPark(TitleSequence& seq, const utf8* path, const utf8* name); + bool TitleSequenceRenamePark(TitleSequence& seq, size_t index, const utf8* name); + bool TitleSequenceRemovePark(TitleSequence& seq, size_t index); - bool IsZip = false; -}; - -struct TitleSequenceParkHandle -{ - std::string HintPath; - std::unique_ptr Stream; -}; - -constexpr const utf8* TITLE_SEQUENCE_EXTENSION = ".parkseq"; -constexpr uint8_t SAVE_INDEX_INVALID = UINT8_MAX; - -[[nodiscard]] std::unique_ptr CreateTitleSequence(); -[[nodiscard]] std::unique_ptr LoadTitleSequence(const std::string& path); -[[nodiscard]] std::unique_ptr TitleSequenceGetParkHandle(const TitleSequence& seq, size_t index); - -bool TitleSequenceSave(const TitleSequence& seq); -bool TitleSequenceAddPark(TitleSequence& seq, const utf8* path, const utf8* name); -bool TitleSequenceRenamePark(TitleSequence& seq, size_t index, const utf8* name); -bool TitleSequenceRemovePark(TitleSequence& seq, size_t index); - -bool TitleSequenceIsLoadCommand(const TitleCommand& command); + bool TitleSequenceIsLoadCommand(const TitleCommand& command); +} // namespace OpenRCT2::Title From 1fc2312abd38c00154087bae22e24067ff9bd4c0 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Sun, 19 Jun 2022 00:18:18 +0200 Subject: [PATCH 03/12] 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; } From 239dcb1677a0512453a68e287f6a830f53fdc531 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Sun, 19 Jun 2022 12:40:02 +0200 Subject: [PATCH 04/12] Update title sequence scripting part --- src/openrct2-ui/scripting/ScTitleSequence.hpp | 168 +++++++++++------- 1 file changed, 105 insertions(+), 63 deletions(-) diff --git a/src/openrct2-ui/scripting/ScTitleSequence.hpp b/src/openrct2-ui/scripting/ScTitleSequence.hpp index 60e0c4b4be..4a6b24addd 100644 --- a/src/openrct2-ui/scripting/ScTitleSequence.hpp +++ b/src/openrct2-ui/scripting/ScTitleSequence.hpp @@ -24,9 +24,28 @@ # include # include # include +# include +# include namespace OpenRCT2::Scripting { + enum class TitleScript : uint8_t + { + Undefined = 0xFF, + Wait = 0, + Location, + Rotate, + Zoom, + Follow, + Restart, + Load, + End, + Speed, + Loop, + EndLoop, + LoadSc, + }; + static const DukEnumMap TitleScriptMap({ { "load", TitleScript::Load }, { "location", TitleScript::Location }, @@ -45,43 +64,59 @@ namespace OpenRCT2::Scripting return ToDuk(ctx, TitleScriptMap[value]); } - template<> DukValue ToDuk(duk_context* ctx, const TitleCommand& value) + template<> DukValue ToDuk(duk_context* ctx, const OpenRCT2::Title::TitleCommand& value) { + using namespace OpenRCT2::Title; DukObject obj(ctx); - obj.Set("type", ToDuk(ctx, value.Type)); - switch (value.Type) - { - case TitleScript::Load: - obj.Set("index", value.SaveIndex); - break; - case TitleScript::Location: - obj.Set("x", value.Location.X); - obj.Set("y", value.Location.Y); - break; - case TitleScript::Rotate: - obj.Set("rotations", value.Rotations); - break; - case TitleScript::Zoom: - obj.Set("zoom", value.Zoom); - break; - case TitleScript::Follow: - if (value.Follow.SpriteIndex.IsNull()) - obj.Set("id", nullptr); - else - obj.Set("id", value.Follow.SpriteIndex.ToUnderlying()); - break; - case TitleScript::Speed: - obj.Set("speed", value.Speed); - break; - case TitleScript::Wait: - obj.Set("duration", value.Milliseconds); - break; - case TitleScript::LoadSc: - obj.Set("scenario", String::ToStringView(value.Scenario, sizeof(value.Scenario))); - break; - default: - break; - } + std::visit( + [&obj](auto&& value) { + using T = std::decay::type; + if constexpr (std::is_same_v) + { + obj.Set("type", TitleScriptMap[TitleScript::Load]); + obj.Set("index", value.SaveIndex); + } + else if constexpr (std::is_same_v) + { + obj.Set("type", TitleScriptMap[TitleScript::Location]); + obj.Set("x", value.Location.X); + obj.Set("y", value.Location.Y); + } + else if constexpr (std::is_same_v) + { + obj.Set("type", TitleScriptMap[TitleScript::Rotate]); + obj.Set("rotations", value.Rotations); + } + else if constexpr (std::is_same_v) + { + obj.Set("type", TitleScriptMap[TitleScript::Zoom]); + obj.Set("zoom", value.Zoom); + } + else if constexpr (std::is_same_v) + { + obj.Set("type", TitleScriptMap[TitleScript::Follow]); + if (value.Follow.SpriteIndex.IsNull()) + obj.Set("id", nullptr); + else + obj.Set("id", value.Follow.SpriteIndex.ToUnderlying()); + } + else if constexpr (std::is_same_v) + { + obj.Set("type", TitleScriptMap[TitleScript::Speed]); + obj.Set("speed", value.Speed); + } + else if constexpr (std::is_same_v) + { + obj.Set("type", TitleScriptMap[TitleScript::Wait]); + obj.Set("duration", value.Milliseconds); + } + else if constexpr (std::is_same_v) + { + obj.Set("type", TitleScriptMap[TitleScript::LoadSc]); + obj.Set("scenario", String::ToStringView(value.Scenario, sizeof(value.Scenario))); + } + }, + value); return obj.Take(); } @@ -92,48 +127,55 @@ namespace OpenRCT2::Scripting throw DukException() << "Invalid title command id"; } - template<> TitleCommand FromDuk(const DukValue& value) + template<> OpenRCT2::Title::TitleCommand FromDuk(const DukValue& value) { + using namespace OpenRCT2::Title; auto type = FromDuk(value["type"]); TitleCommand command{}; - command.Type = type; switch (type) { case TitleScript::Load: - command.SaveIndex = value["index"].as_int(); + command = LoadParkCommand{ static_cast(value["index"].as_int()) }; break; case TitleScript::Location: - command.Location.X = value["x"].as_int(); - command.Location.Y = value["y"].as_int(); + command = SetLocationCommand{ + static_cast(value["x"].as_int()), + static_cast(value["y"].as_int()), + }; break; case TitleScript::Rotate: - command.Rotations = value["rotations"].as_int(); + command = RotateViewCommand{ static_cast(value["rotations"].as_int()) }; break; case TitleScript::Zoom: - command.Zoom = value["zoom"].as_int(); + command = SetZoomCommand{ static_cast(value["zoom"].as_int()) }; break; case TitleScript::Follow: { auto dukId = value["id"]; if (dukId.type() == DukValue::Type::NUMBER) { - command.Follow.SpriteIndex = EntityId::FromUnderlying(dukId.as_int()); + command = FollowEntityCommand{ EntityId::FromUnderlying(dukId.as_int()) }; } else { - command.Follow.SpriteIndex = EntityId::GetNull(); + command = FollowEntityCommand{ EntityId::GetNull() }; } break; } case TitleScript::Speed: - command.Speed = value["speed"].as_int(); + command = SetSpeedCommand{ static_cast(value["speed"].as_int()) }; break; case TitleScript::Wait: - command.Milliseconds = value["duration"].as_int(); + command = WaitCommand{ static_cast(value["duration"].as_int()) }; break; case TitleScript::LoadSc: - String::Set(command.Scenario, sizeof(command.Scenario), value["scenario"].as_c_string()); + { + auto loadScenarioCommand = LoadScenarioCommand{}; + String::Set( + loadScenarioCommand.Scenario, sizeof(loadScenarioCommand.Scenario), value["scenario"].as_c_string()); + command = loadScenarioCommand; break; + } default: break; } @@ -164,7 +206,7 @@ namespace OpenRCT2::Scripting if (value == _fileName) return; - auto seq = LoadTitleSequence(_titleSequencePath); + auto seq = OpenRCT2::Title::LoadTitleSequence(_titleSequencePath); if (seq != nullptr) { // Check if name already in use @@ -183,27 +225,27 @@ namespace OpenRCT2::Scripting void delete_() { - auto seq = LoadTitleSequence(_titleSequencePath); + auto seq = OpenRCT2::Title::LoadTitleSequence(_titleSequencePath); if (seq != nullptr) { auto index = GetIndex(*seq, _fileName); if (index) { - TitleSequenceRemovePark(*seq, *index); - TitleSequenceSave(*seq); + OpenRCT2::Title::TitleSequenceRemovePark(*seq, *index); + OpenRCT2::Title::TitleSequenceSave(*seq); } } } void load() { - auto seq = LoadTitleSequence(_titleSequencePath); + auto seq = OpenRCT2::Title::LoadTitleSequence(_titleSequencePath); if (seq != nullptr) { auto index = GetIndex(*seq, _fileName); if (index) { - auto handle = TitleSequenceGetParkHandle(*seq, *index); + auto handle = OpenRCT2::Title::TitleSequenceGetParkHandle(*seq, *index); auto isScenario = ParkImporter::ExtensionIsScenario(handle->HintPath); try { @@ -246,7 +288,7 @@ namespace OpenRCT2::Scripting } private: - static std::optional GetIndex(const TitleSequence& seq, const std::string_view needle) + static std::optional GetIndex(const OpenRCT2::Title::TitleSequence& seq, const std::string_view needle) { for (size_t i = 0; i < seq.Saves.size(); i++) { @@ -327,7 +369,7 @@ namespace OpenRCT2::Scripting std::vector> parks_get() const { std::vector> result; - auto titleSeq = LoadTitleSequence(_path); + auto titleSeq = OpenRCT2::Title::LoadTitleSequence(_path); if (titleSeq != nullptr) { for (size_t i = 0; i < titleSeq->Saves.size(); i++) @@ -344,7 +386,7 @@ namespace OpenRCT2::Scripting auto ctx = scriptEngine.GetContext(); std::vector result; - auto titleSeq = LoadTitleSequence(_path); + auto titleSeq = OpenRCT2::Title::LoadTitleSequence(_path); if (titleSeq != nullptr) { for (const auto& command : titleSeq->Commands) @@ -357,23 +399,23 @@ namespace OpenRCT2::Scripting void commands_set(const std::vector& value) { - std::vector commands; + std::vector commands; for (const auto& v : value) { - auto command = FromDuk(v); + auto command = FromDuk(v); commands.push_back(std::move(command)); } - auto titleSeq = LoadTitleSequence(_path); + auto titleSeq = OpenRCT2::Title::LoadTitleSequence(_path); titleSeq->Commands = commands; - TitleSequenceSave(*titleSeq); + OpenRCT2::Title::TitleSequenceSave(*titleSeq); } void addPark(const std::string& path, const std::string& fileName) { - auto titleSeq = LoadTitleSequence(_path); - TitleSequenceAddPark(*titleSeq, path.c_str(), fileName.c_str()); - TitleSequenceSave(*titleSeq); + auto titleSeq = OpenRCT2::Title::LoadTitleSequence(_path); + OpenRCT2::Title::TitleSequenceAddPark(*titleSeq, path.c_str(), fileName.c_str()); + OpenRCT2::Title::TitleSequenceSave(*titleSeq); } std::shared_ptr clone(const std::string& name) const From 3da305b82f5f69754e48d5c8a4b9fdde474eda31 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Sun, 19 Jun 2022 19:19:20 +0200 Subject: [PATCH 05/12] Implement follow entity command --- src/openrct2-ui/title/TitleSequencePlayer.cpp | 18 ------------------ src/openrct2/title/Command/FollowEntity.cpp | 8 +++++++- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/openrct2-ui/title/TitleSequencePlayer.cpp b/src/openrct2-ui/title/TitleSequencePlayer.cpp index 91c6c37a89..3d413ab3cc 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.cpp +++ b/src/openrct2-ui/title/TitleSequencePlayer.cpp @@ -262,24 +262,6 @@ namespace OpenRCT2::Title 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); diff --git a/src/openrct2/title/Command/FollowEntity.cpp b/src/openrct2/title/Command/FollowEntity.cpp index 5db67999a9..83d4625bca 100644 --- a/src/openrct2/title/Command/FollowEntity.cpp +++ b/src/openrct2/title/Command/FollowEntity.cpp @@ -9,11 +9,17 @@ #include "FollowEntity.h" +#include "../../interface/Window.h" + namespace OpenRCT2::Title { int16_t FollowEntityCommand::operator()(int16_t timer) { - // TODO: Set entity to follow + auto* w = window_get_main(); + if (w != nullptr) + { + window_follow_sprite(w, Follow.SpriteIndex); + } return 0; } From c8537d57b33d43e9ae9ba1febaf0718bf5304319 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Mon, 20 Jun 2022 19:14:47 +0200 Subject: [PATCH 06/12] Note for commands used as tags --- src/openrct2/title/Command/End.cpp | 1 + src/openrct2/title/Command/Restart.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/openrct2/title/Command/End.cpp b/src/openrct2/title/Command/End.cpp index 2249a42b1d..467cc28bd5 100644 --- a/src/openrct2/title/Command/End.cpp +++ b/src/openrct2/title/Command/End.cpp @@ -13,6 +13,7 @@ namespace OpenRCT2::Title { int16_t EndCommand::operator()(int16_t timer) { + // The end command is used as a tag, no logic required here. return 0; } } // namespace OpenRCT2::Title diff --git a/src/openrct2/title/Command/Restart.cpp b/src/openrct2/title/Command/Restart.cpp index 07dd2b7204..4473ae48f6 100644 --- a/src/openrct2/title/Command/Restart.cpp +++ b/src/openrct2/title/Command/Restart.cpp @@ -13,6 +13,7 @@ namespace OpenRCT2::Title { int16_t RestartCommand::operator()(int16_t timer) { + // The restart command is used as a tag, no logic required here. return 0; } } // namespace OpenRCT2::Title From 2b52df87ceb68155f0745e8997dedff96772c67d Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Mon, 20 Jun 2022 21:08:37 +0200 Subject: [PATCH 07/12] Move scripting command names to command classes This way, new commands are required to have this field, or a compiler error will occur. --- src/openrct2-ui/scripting/ScTitleSequence.hpp | 29 +++++++------------ src/openrct2/scripting/ScriptEngine.h | 2 +- src/openrct2/title/Command/End.h | 1 + src/openrct2/title/Command/FollowEntity.h | 1 + src/openrct2/title/Command/LoadPark.h | 1 + src/openrct2/title/Command/LoadScenario.h | 1 + src/openrct2/title/Command/Restart.h | 1 + src/openrct2/title/Command/RotateView.h | 1 + src/openrct2/title/Command/SetLocation.h | 1 + src/openrct2/title/Command/SetSpeed.h | 1 + src/openrct2/title/Command/SetZoom.h | 1 + src/openrct2/title/Command/Wait.h | 1 + 12 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/openrct2-ui/scripting/ScTitleSequence.hpp b/src/openrct2-ui/scripting/ScTitleSequence.hpp index 4a6b24addd..566cc8ad99 100644 --- a/src/openrct2-ui/scripting/ScTitleSequence.hpp +++ b/src/openrct2-ui/scripting/ScTitleSequence.hpp @@ -47,16 +47,16 @@ namespace OpenRCT2::Scripting }; static const DukEnumMap TitleScriptMap({ - { "load", TitleScript::Load }, - { "location", TitleScript::Location }, - { "rotate", TitleScript::Rotate }, - { "zoom", TitleScript::Zoom }, - { "follow", TitleScript::Follow }, - { "speed", TitleScript::Speed }, - { "wait", TitleScript::Wait }, - { "loadsc", TitleScript::LoadSc }, - { "restart", TitleScript::Restart }, - { "end", TitleScript::End }, + { OpenRCT2::Title::LoadParkCommand::ScriptingName, TitleScript::Load }, + { OpenRCT2::Title::SetLocationCommand::ScriptingName, TitleScript::Location }, + { OpenRCT2::Title::RotateViewCommand::ScriptingName, TitleScript::Rotate }, + { OpenRCT2::Title::SetZoomCommand::ScriptingName, TitleScript::Zoom }, + { OpenRCT2::Title::FollowEntityCommand::ScriptingName, TitleScript::Follow }, + { OpenRCT2::Title::SetSpeedCommand::ScriptingName, TitleScript::Speed }, + { OpenRCT2::Title::WaitCommand::ScriptingName, TitleScript::Wait }, + { OpenRCT2::Title::LoadScenarioCommand::ScriptingName, TitleScript::LoadSc }, + { OpenRCT2::Title::RestartCommand::ScriptingName, TitleScript::Restart }, + { OpenRCT2::Title::EndCommand::ScriptingName, TitleScript::End }, }); template<> DukValue ToDuk(duk_context* ctx, const TitleScript& value) @@ -71,30 +71,26 @@ namespace OpenRCT2::Scripting std::visit( [&obj](auto&& value) { using T = std::decay::type; + obj.Set("type", T::ScriptingName); if constexpr (std::is_same_v) { - obj.Set("type", TitleScriptMap[TitleScript::Load]); obj.Set("index", value.SaveIndex); } else if constexpr (std::is_same_v) { - obj.Set("type", TitleScriptMap[TitleScript::Location]); obj.Set("x", value.Location.X); obj.Set("y", value.Location.Y); } else if constexpr (std::is_same_v) { - obj.Set("type", TitleScriptMap[TitleScript::Rotate]); obj.Set("rotations", value.Rotations); } else if constexpr (std::is_same_v) { - obj.Set("type", TitleScriptMap[TitleScript::Zoom]); obj.Set("zoom", value.Zoom); } else if constexpr (std::is_same_v) { - obj.Set("type", TitleScriptMap[TitleScript::Follow]); if (value.Follow.SpriteIndex.IsNull()) obj.Set("id", nullptr); else @@ -102,17 +98,14 @@ namespace OpenRCT2::Scripting } else if constexpr (std::is_same_v) { - obj.Set("type", TitleScriptMap[TitleScript::Speed]); obj.Set("speed", value.Speed); } else if constexpr (std::is_same_v) { - obj.Set("type", TitleScriptMap[TitleScript::Wait]); obj.Set("duration", value.Milliseconds); } else if constexpr (std::is_same_v) { - obj.Set("type", TitleScriptMap[TitleScript::LoadSc]); obj.Set("scenario", String::ToStringView(value.Scenario, sizeof(value.Scenario))); } }, diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index 4ea7c497cd..7e162cbc28 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -46,7 +46,7 @@ namespace OpenRCT2 namespace OpenRCT2::Scripting { - static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 56; + static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 57; // Versions marking breaking changes. static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33; diff --git a/src/openrct2/title/Command/End.h b/src/openrct2/title/Command/End.h index f6bda4a516..9f40acefe3 100644 --- a/src/openrct2/title/Command/End.h +++ b/src/openrct2/title/Command/End.h @@ -16,6 +16,7 @@ namespace OpenRCT2::Title struct EndCommand { static constexpr const char* Name = "End Command"; + static constexpr const char* ScriptingName = "end"; int16_t operator()(int16_t timer); }; diff --git a/src/openrct2/title/Command/FollowEntity.h b/src/openrct2/title/Command/FollowEntity.h index 9224eb3a90..e76b6c7eff 100644 --- a/src/openrct2/title/Command/FollowEntity.h +++ b/src/openrct2/title/Command/FollowEntity.h @@ -20,6 +20,7 @@ namespace OpenRCT2::Title struct FollowEntityCommand { static constexpr const char* Name = "Follow Entity Command"; + static constexpr const char* ScriptingName = "follow"; struct { diff --git a/src/openrct2/title/Command/LoadPark.h b/src/openrct2/title/Command/LoadPark.h index e2569a0c2b..c2c2986aec 100644 --- a/src/openrct2/title/Command/LoadPark.h +++ b/src/openrct2/title/Command/LoadPark.h @@ -16,6 +16,7 @@ namespace OpenRCT2::Title struct LoadParkCommand { static constexpr const char* Name = "Load Park Command"; + static constexpr const char* ScriptingName = "load"; uint8_t SaveIndex; diff --git a/src/openrct2/title/Command/LoadScenario.h b/src/openrct2/title/Command/LoadScenario.h index b27bbbafe5..27fa347481 100644 --- a/src/openrct2/title/Command/LoadScenario.h +++ b/src/openrct2/title/Command/LoadScenario.h @@ -20,6 +20,7 @@ namespace OpenRCT2::Title struct LoadScenarioCommand { static constexpr const char* Name = "Load Scenario Command"; + static constexpr const char* ScriptingName = "loadsc"; utf8 Scenario[TITLE_COMMAND_SCENARIO_LENGTH]; diff --git a/src/openrct2/title/Command/Restart.h b/src/openrct2/title/Command/Restart.h index 95e2494c6d..45c76af956 100644 --- a/src/openrct2/title/Command/Restart.h +++ b/src/openrct2/title/Command/Restart.h @@ -16,6 +16,7 @@ namespace OpenRCT2::Title struct RestartCommand { static constexpr const char* Name = "Restart Command"; + static constexpr const char* ScriptingName = "restart"; int16_t operator()(int16_t timer); }; diff --git a/src/openrct2/title/Command/RotateView.h b/src/openrct2/title/Command/RotateView.h index 91c52f46e2..87253c03bd 100644 --- a/src/openrct2/title/Command/RotateView.h +++ b/src/openrct2/title/Command/RotateView.h @@ -16,6 +16,7 @@ namespace OpenRCT2::Title struct RotateViewCommand { static constexpr const char* Name = "Rotate View Command"; + static constexpr const char* ScriptingName = "rotate"; uint8_t Rotations; diff --git a/src/openrct2/title/Command/SetLocation.h b/src/openrct2/title/Command/SetLocation.h index fef8b52db2..639e47e043 100644 --- a/src/openrct2/title/Command/SetLocation.h +++ b/src/openrct2/title/Command/SetLocation.h @@ -16,6 +16,7 @@ namespace OpenRCT2::Title struct SetLocationCommand { static constexpr const char* Name = "Set Location Command"; + static constexpr const char* ScriptingName = "location"; // TODO: Use TileCoordsXY instead struct diff --git a/src/openrct2/title/Command/SetSpeed.h b/src/openrct2/title/Command/SetSpeed.h index 96c97a32b1..01297cbba9 100644 --- a/src/openrct2/title/Command/SetSpeed.h +++ b/src/openrct2/title/Command/SetSpeed.h @@ -16,6 +16,7 @@ namespace OpenRCT2::Title struct SetSpeedCommand { static constexpr const char* Name = "Set Speed Command"; + static constexpr const char* ScriptingName = "speed"; uint8_t Speed; diff --git a/src/openrct2/title/Command/SetZoom.h b/src/openrct2/title/Command/SetZoom.h index 8c78f3fdde..e0347c39b9 100644 --- a/src/openrct2/title/Command/SetZoom.h +++ b/src/openrct2/title/Command/SetZoom.h @@ -16,6 +16,7 @@ namespace OpenRCT2::Title struct SetZoomCommand { static constexpr const char* Name = "Set Zoom Command"; + static constexpr const char* ScriptingName = "zoom"; // TODO: Use ZoomLevel instead uint8_t Zoom; diff --git a/src/openrct2/title/Command/Wait.h b/src/openrct2/title/Command/Wait.h index c14317c9d3..1da57e5b48 100644 --- a/src/openrct2/title/Command/Wait.h +++ b/src/openrct2/title/Command/Wait.h @@ -16,6 +16,7 @@ namespace OpenRCT2::Title struct WaitCommand { static constexpr const char* Name = "Wait Command"; + static constexpr const char* ScriptingName = "wait"; uint16_t Milliseconds; From 43f49f2483295eaaaa33378f1c3b5681df06b147 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Tue, 21 Jun 2022 21:04:11 +0200 Subject: [PATCH 08/12] Fix: commands used as tags don't save in scripting --- src/openrct2-ui/scripting/ScTitleSequence.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/openrct2-ui/scripting/ScTitleSequence.hpp b/src/openrct2-ui/scripting/ScTitleSequence.hpp index 566cc8ad99..156f7f5ed2 100644 --- a/src/openrct2-ui/scripting/ScTitleSequence.hpp +++ b/src/openrct2-ui/scripting/ScTitleSequence.hpp @@ -169,6 +169,12 @@ namespace OpenRCT2::Scripting command = loadScenarioCommand; break; } + case TitleScript::Restart: + command = RestartCommand{}; + break; + case TitleScript::End: + command = EndCommand{}; + break; default: break; } From 0e5cfce073e0663787ffb4fb7387e919122d8096 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Tue, 21 Jun 2022 22:04:53 +0200 Subject: [PATCH 09/12] Maintain centre view position when screen resizes --- src/openrct2-ui/title/TitleSequencePlayer.cpp | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/openrct2-ui/title/TitleSequencePlayer.cpp b/src/openrct2-ui/title/TitleSequencePlayer.cpp index 3d413ab3cc..93fd85c828 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.cpp +++ b/src/openrct2-ui/title/TitleSequencePlayer.cpp @@ -52,9 +52,9 @@ namespace OpenRCT2::Title int32_t _position = 0; int32_t _waitCounter = 0; - int32_t _lastScreenWidth = 0; - int32_t _lastScreenHeight = 0; - CoordsXY _viewCentreLocation = {}; + int32_t _previousWindowWidth = 0; + int32_t _previousWindowHeight = 0; + ScreenCoordsXY _previousViewPosition = {}; public: explicit TitleSequencePlayer(GameState& gameState) @@ -79,6 +79,8 @@ namespace OpenRCT2::Title bool Begin(size_t titleSequenceId) override { + StoreCurrentViewLocation(); + size_t numSequences = TitleSequenceManager::GetCount(); if (titleSequenceId >= numSequences) { @@ -101,7 +103,7 @@ namespace OpenRCT2::Title bool Update() override { - FixViewLocation(); + RestoreViewLocationIfResized(); if (_sequence == nullptr) { @@ -119,7 +121,7 @@ namespace OpenRCT2::Title if (framesToWait > _waitCounter) { _waitCounter++; - return true; + break; } // TODO: Make the loading interface simpler so these blocks can be moved to their respective command classes @@ -183,6 +185,11 @@ namespace OpenRCT2::Title return false; } } + + // Store current window size and screen position in case the window resizes and the main focus changes + StoreCurrentViewLocation(); + + return true; } void Reset() override @@ -373,17 +380,29 @@ namespace OpenRCT2::Title gGameSpeed = 1; } - /** - * Fixes the view location for when the game window has changed size. - */ - void FixViewLocation() + void StoreCurrentViewLocation() { rct_window* w = window_get_main(); if (w != nullptr && w->viewport_smart_follow_sprite.IsNull()) { - if (w->width != _lastScreenWidth || w->height != _lastScreenHeight) + _previousWindowWidth = w->width; + _previousWindowHeight = w->height; + _previousViewPosition = w->savedViewPos; + } + } + + /** + * Fixes the view location for when the game window has changed size. + */ + void RestoreViewLocationIfResized() + { + rct_window* w = window_get_main(); + if (w != nullptr && w->viewport_smart_follow_sprite.IsNull()) + { + if (w->width != _previousWindowWidth || w->height != _previousWindowHeight) { - // SetViewLocation(_viewCentreLocation); + w->savedViewPos.x += (_previousWindowWidth - w->width) / 2; + w->savedViewPos.y += (_previousWindowHeight - w->height) / 2; } } } From 6a017ac76136725238e0c99a8acd4e12d705ceae Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Tue, 21 Jun 2022 22:30:39 +0200 Subject: [PATCH 10/12] use std::decay helper type --- src/openrct2-ui/scripting/ScTitleSequence.hpp | 2 +- src/openrct2-ui/title/TitleSequencePlayer.cpp | 2 +- src/openrct2/title/TitleSequence.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/openrct2-ui/scripting/ScTitleSequence.hpp b/src/openrct2-ui/scripting/ScTitleSequence.hpp index 156f7f5ed2..593f4fbd5e 100644 --- a/src/openrct2-ui/scripting/ScTitleSequence.hpp +++ b/src/openrct2-ui/scripting/ScTitleSequence.hpp @@ -70,7 +70,7 @@ namespace OpenRCT2::Scripting DukObject obj(ctx); std::visit( [&obj](auto&& value) { - using T = std::decay::type; + using T = std::decay_t; obj.Set("type", T::ScriptingName); if constexpr (std::is_same_v) { diff --git a/src/openrct2-ui/title/TitleSequencePlayer.cpp b/src/openrct2-ui/title/TitleSequencePlayer.cpp index 93fd85c828..cde1ca3c7a 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.cpp +++ b/src/openrct2-ui/title/TitleSequencePlayer.cpp @@ -171,7 +171,7 @@ namespace OpenRCT2::Title catch (std::exception& e) { const char* commandName = std::visit( - [](auto&& command) { return std::decay::type::Name; }, currentCommand); + [](auto&& command) { return std::decay_t::Name; }, currentCommand); Console::Error::WriteLine("%s (command %i) failed with error: %s", commandName, _position, e.what()); Console::Error::WriteLine(" Skipping to the next command."); } diff --git a/src/openrct2/title/TitleSequence.cpp b/src/openrct2/title/TitleSequence.cpp index 4fe53732ca..9402d25d8e 100644 --- a/src/openrct2/title/TitleSequence.cpp +++ b/src/openrct2/title/TitleSequence.cpp @@ -279,7 +279,7 @@ namespace OpenRCT2::Title { std::visit( [index](auto&& command) { - if constexpr (std::is_same_v, LoadParkCommand>) + if constexpr (std::is_same_v, LoadParkCommand>) { if (command.SaveIndex == index) { @@ -511,7 +511,7 @@ namespace OpenRCT2::Title { std::visit( [&buffer, &seq, &sb](auto&& command) { - using T = std::decay::type; + using T = std::decay_t; if constexpr (std::is_same_v) { if (command.SaveIndex < seq.Saves.size()) From 8e439b98950b09f9578c59c59650331b0633d011 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Tue, 21 Jun 2022 22:48:29 +0200 Subject: [PATCH 11/12] Zero-initialize all command arguments --- src/openrct2/title/Command/FollowEntity.h | 4 ++-- src/openrct2/title/Command/LoadPark.h | 2 +- src/openrct2/title/Command/LoadScenario.h | 2 +- src/openrct2/title/Command/RotateView.h | 2 +- src/openrct2/title/Command/SetSpeed.h | 2 +- src/openrct2/title/Command/SetZoom.h | 2 +- src/openrct2/title/Command/Wait.h | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/openrct2/title/Command/FollowEntity.h b/src/openrct2/title/Command/FollowEntity.h index e76b6c7eff..0fbb7a5908 100644 --- a/src/openrct2/title/Command/FollowEntity.h +++ b/src/openrct2/title/Command/FollowEntity.h @@ -24,8 +24,8 @@ namespace OpenRCT2::Title struct { - EntityId SpriteIndex; - utf8 SpriteName[USER_STRING_MAX_LENGTH]; + EntityId SpriteIndex{ EntityId::GetNull() }; + utf8 SpriteName[USER_STRING_MAX_LENGTH]{}; } Follow; int16_t operator()(int16_t timer); diff --git a/src/openrct2/title/Command/LoadPark.h b/src/openrct2/title/Command/LoadPark.h index c2c2986aec..8198f8a184 100644 --- a/src/openrct2/title/Command/LoadPark.h +++ b/src/openrct2/title/Command/LoadPark.h @@ -18,7 +18,7 @@ namespace OpenRCT2::Title static constexpr const char* Name = "Load Park Command"; static constexpr const char* ScriptingName = "load"; - uint8_t SaveIndex; + uint8_t SaveIndex{}; int16_t operator()(int16_t timer); }; diff --git a/src/openrct2/title/Command/LoadScenario.h b/src/openrct2/title/Command/LoadScenario.h index 27fa347481..62b96e90e7 100644 --- a/src/openrct2/title/Command/LoadScenario.h +++ b/src/openrct2/title/Command/LoadScenario.h @@ -22,7 +22,7 @@ namespace OpenRCT2::Title static constexpr const char* Name = "Load Scenario Command"; static constexpr const char* ScriptingName = "loadsc"; - utf8 Scenario[TITLE_COMMAND_SCENARIO_LENGTH]; + utf8 Scenario[TITLE_COMMAND_SCENARIO_LENGTH]{}; int16_t operator()(int16_t timer); }; diff --git a/src/openrct2/title/Command/RotateView.h b/src/openrct2/title/Command/RotateView.h index 87253c03bd..c3bf0bb423 100644 --- a/src/openrct2/title/Command/RotateView.h +++ b/src/openrct2/title/Command/RotateView.h @@ -18,7 +18,7 @@ namespace OpenRCT2::Title static constexpr const char* Name = "Rotate View Command"; static constexpr const char* ScriptingName = "rotate"; - uint8_t Rotations; + uint8_t Rotations{}; int16_t operator()(int16_t timer); }; diff --git a/src/openrct2/title/Command/SetSpeed.h b/src/openrct2/title/Command/SetSpeed.h index 01297cbba9..36d5e7daf0 100644 --- a/src/openrct2/title/Command/SetSpeed.h +++ b/src/openrct2/title/Command/SetSpeed.h @@ -18,7 +18,7 @@ namespace OpenRCT2::Title static constexpr const char* Name = "Set Speed Command"; static constexpr const char* ScriptingName = "speed"; - uint8_t Speed; + uint8_t Speed{}; int16_t operator()(int16_t timer); }; diff --git a/src/openrct2/title/Command/SetZoom.h b/src/openrct2/title/Command/SetZoom.h index e0347c39b9..70374fac86 100644 --- a/src/openrct2/title/Command/SetZoom.h +++ b/src/openrct2/title/Command/SetZoom.h @@ -19,7 +19,7 @@ namespace OpenRCT2::Title static constexpr const char* ScriptingName = "zoom"; // TODO: Use ZoomLevel instead - uint8_t Zoom; + uint8_t Zoom{}; int16_t operator()(int16_t timer); }; diff --git a/src/openrct2/title/Command/Wait.h b/src/openrct2/title/Command/Wait.h index 1da57e5b48..9bc216955c 100644 --- a/src/openrct2/title/Command/Wait.h +++ b/src/openrct2/title/Command/Wait.h @@ -18,7 +18,7 @@ namespace OpenRCT2::Title static constexpr const char* Name = "Wait Command"; static constexpr const char* ScriptingName = "wait"; - uint16_t Milliseconds; + uint16_t Milliseconds{}; int16_t operator()(int16_t timer); }; From 79ae03fc5efa6229fc6aa11d479ad782d6ba0aa6 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Tue, 21 Jun 2022 23:07:16 +0200 Subject: [PATCH 12/12] Rename `command` to avoid ghosting It's curious that gcc complains about ghosting here, since the lambda has its own stack pointer, and doesn't capture the outer `command`. --- src/openrct2-ui/scripting/ScTitleSequence.hpp | 24 +++++++++---------- src/openrct2/title/TitleSequence.cpp | 8 +++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/openrct2-ui/scripting/ScTitleSequence.hpp b/src/openrct2-ui/scripting/ScTitleSequence.hpp index 593f4fbd5e..c2e61a04bf 100644 --- a/src/openrct2-ui/scripting/ScTitleSequence.hpp +++ b/src/openrct2-ui/scripting/ScTitleSequence.hpp @@ -69,44 +69,44 @@ namespace OpenRCT2::Scripting using namespace OpenRCT2::Title; DukObject obj(ctx); std::visit( - [&obj](auto&& value) { - using T = std::decay_t; + [&obj](auto&& command) { + using T = std::decay_t; obj.Set("type", T::ScriptingName); if constexpr (std::is_same_v) { - obj.Set("index", value.SaveIndex); + obj.Set("index", command.SaveIndex); } else if constexpr (std::is_same_v) { - obj.Set("x", value.Location.X); - obj.Set("y", value.Location.Y); + obj.Set("x", command.Location.X); + obj.Set("y", command.Location.Y); } else if constexpr (std::is_same_v) { - obj.Set("rotations", value.Rotations); + obj.Set("rotations", command.Rotations); } else if constexpr (std::is_same_v) { - obj.Set("zoom", value.Zoom); + obj.Set("zoom", command.Zoom); } else if constexpr (std::is_same_v) { - if (value.Follow.SpriteIndex.IsNull()) + if (command.Follow.SpriteIndex.IsNull()) obj.Set("id", nullptr); else - obj.Set("id", value.Follow.SpriteIndex.ToUnderlying()); + obj.Set("id", command.Follow.SpriteIndex.ToUnderlying()); } else if constexpr (std::is_same_v) { - obj.Set("speed", value.Speed); + obj.Set("speed", command.Speed); } else if constexpr (std::is_same_v) { - obj.Set("duration", value.Milliseconds); + obj.Set("duration", command.Milliseconds); } else if constexpr (std::is_same_v) { - obj.Set("scenario", String::ToStringView(value.Scenario, sizeof(value.Scenario))); + obj.Set("scenario", String::ToStringView(command.Scenario, sizeof(command.Scenario))); } }, value); diff --git a/src/openrct2/title/TitleSequence.cpp b/src/openrct2/title/TitleSequence.cpp index 9402d25d8e..35bc7c1fed 100644 --- a/src/openrct2/title/TitleSequence.cpp +++ b/src/openrct2/title/TitleSequence.cpp @@ -275,7 +275,7 @@ namespace OpenRCT2::Title seq.Saves.erase(seq.Saves.begin() + index); // Update load commands - for (auto& command : seq.Commands) + for (auto& seqCommand : seq.Commands) { std::visit( [index](auto&& command) { @@ -293,7 +293,7 @@ namespace OpenRCT2::Title } } }, - command); + seqCommand); } return true; @@ -507,7 +507,7 @@ namespace OpenRCT2::Title sb.Append("# SCRIPT FOR "); sb.Append(seq.Name.c_str()); sb.Append("\n"); - for (const auto& command : seq.Commands) + for (const auto& seqCommand : seq.Commands) { std::visit( [&buffer, &seq, &sb](auto&& command) { @@ -576,7 +576,7 @@ namespace OpenRCT2::Title sb.Append("END"); } }, - command); + seqCommand); sb.Append("\n"); }