diff --git a/.vscode/settings.json b/.vscode/settings.json index 17a6f4e..7e40a45 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -124,7 +124,8 @@ "__std_stream": "cpp", "memory_resource": "cpp", "ranges": "cpp", - "__verbose_abort": "cpp" + "__verbose_abort": "cpp", + "numbers": "cpp" }, "editor.formatOnSave": false, "editor.trimAutoWhitespace": true, diff --git a/mod.json b/mod.json index f814e01..f66b260 100644 --- a/mod.json +++ b/mod.json @@ -4,31 +4,31 @@ "id": "chatplex-sdk-bs", "modloader": "Scotland2", "author": "HardCPP", - "version": "6.3.1", + "version": "6.3.2", "packageId": "com.beatgames.beatsaber", - "packageVersion": "1.35.0_8016709773", + "packageVersion": "1.37.0_9064817954", "description": "ChatPlex BeatSaber modding SDK (Dependence for other mods)", "coverImage": "cover.png", "dependencies": [ { - "version": "^0.17.6", + "version": "^0.17.8", "id": "custom-types", - "downloadIfMissing": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.17.8/CustomTypes.qmod" + "downloadIfMissing": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.17.10/CustomTypes.qmod" }, { - "version": "^0.4.20", + "version": "^0.4.34", "id": "bsml", - "downloadIfMissing": "https://github.com/RedBrumbler/Quest-BSML/releases/download/v0.4.41/BSML.qmod" + "downloadIfMissing": "https://github.com/RedBrumbler/Quest-BSML/releases/download/v0.4.43/BSML.qmod" }, { - "version": "^1.1.9", + "version": "^1.1.12", "id": "songcore", - "downloadIfMissing": "https://github.com/raineio/Quest-SongCore/releases/download/v1.1.9/SongCore.qmod" + "downloadIfMissing": "https://github.com/raineio/Quest-SongCore/releases/download/v1.1.15/SongCore.qmod" }, { "version": "^3.6.3", "id": "paper", - "downloadIfMissing": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.3/paperlog.qmod" + "downloadIfMissing": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.4/paperlog.qmod" } ], "modFiles": [], @@ -36,7 +36,7 @@ "libchatplex-sdk-bs.so" ], "libraryFiles": [ - "libbeatsaber-hook_5_1_7.so" + "libbeatsaber-hook_5_1_9.so" ], "fileCopies": [], "copyExtensions": [] diff --git a/mod.template.json b/mod.template.json index 6b636a6..74565b0 100644 --- a/mod.template.json +++ b/mod.template.json @@ -6,7 +6,7 @@ "author": "HardCPP", "version": "${version}", "packageId": "com.beatgames.beatsaber", - "packageVersion": "1.35.0_8016709773", + "packageVersion": "1.37.0_9064817954", "description": "ChatPlex BeatSaber modding SDK (Dependence for other mods)", "coverImage": "cover.png", "dependencies": [], diff --git a/qpm.json b/qpm.json index 8a7c889..b96c1d0 100644 --- a/qpm.json +++ b/qpm.json @@ -5,7 +5,7 @@ "info": { "name": "ChatPlexSDK-BS", "id": "chatplex-sdk-bs", - "version": "6.3.1", + "version": "6.3.2", "url": "https://github.com/hardcpp/QuestChatPlexSDK-BS", "additionalData": { "overrideSoName": "libchatplex-sdk-bs.so", @@ -40,17 +40,17 @@ "dependencies": [ { "id": "beatsaber-hook", - "versionRange": "^5.1.6", + "versionRange": "^5.1.9", "additionalData": {} }, { "id": "bs-cordl", - "versionRange": "^3500.0.0", + "versionRange": "^3700.*", "additionalData": {} }, { "id": "custom-types", - "versionRange": "^0.17.6", + "versionRange": "^0.17.8", "additionalData": {} }, { @@ -63,7 +63,7 @@ }, { "id": "bsml", - "versionRange": "^0.4.20", + "versionRange": "^0.4.34", "additionalData": { "private": true } @@ -75,7 +75,7 @@ }, { "id": "songcore", - "versionRange": "^1.1.9", + "versionRange": "^1.1.12", "additionalData": { "private": true } diff --git a/qpm.shared.json b/qpm.shared.json index 65485f0..b6ff0ad 100644 --- a/qpm.shared.json +++ b/qpm.shared.json @@ -6,7 +6,7 @@ "info": { "name": "ChatPlexSDK-BS", "id": "chatplex-sdk-bs", - "version": "6.3.1", + "version": "6.3.2", "url": "https://github.com/hardcpp/QuestChatPlexSDK-BS", "additionalData": { "overrideSoName": "libchatplex-sdk-bs.so", @@ -44,17 +44,17 @@ "dependencies": [ { "id": "beatsaber-hook", - "versionRange": "^5.1.6", + "versionRange": "^5.1.9", "additionalData": {} }, { "id": "bs-cordl", - "versionRange": "^3500.0.0", + "versionRange": "3700.*", "additionalData": {} }, { "id": "custom-types", - "versionRange": "^0.17.6", + "versionRange": "^0.17.8", "additionalData": {} }, { @@ -67,7 +67,7 @@ }, { "id": "bsml", - "versionRange": "^0.4.20", + "versionRange": "^0.4.34", "additionalData": { "private": true } @@ -79,7 +79,7 @@ }, { "id": "songcore", - "versionRange": "^1.1.9", + "versionRange": "^1.1.12", "additionalData": { "private": true } @@ -107,13 +107,13 @@ { "dependency": { "id": "paper", - "versionRange": "=3.6.3", + "versionRange": "=3.6.4", "additionalData": { - "soLink": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.3/libpaperlog.so", - "debugSoLink": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.3/debug_libpaperlog.so", + "soLink": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.4/libpaperlog.so", + "debugSoLink": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.4/debug_libpaperlog.so", "overrideSoName": "libpaperlog.so", - "modLink": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.3/paperlog.qmod", - "branchName": "version/v3_6_3", + "modLink": "https://github.com/Fernthedev/paperlog/releases/download/v3.6.4/paperlog.qmod", + "branchName": "version/v3_6_4", "compileOptions": { "systemIncludes": [ "shared/utfcpp/source" @@ -122,7 +122,7 @@ "cmake": false } }, - "version": "3.6.3" + "version": "3.6.4" }, { "dependency": { @@ -138,28 +138,28 @@ { "dependency": { "id": "bsml", - "versionRange": "=0.4.41", + "versionRange": "=0.4.43", "additionalData": { - "soLink": "https://github.com/RedBrumbler/Quest-BSML/releases/download/v0.4.41/libbsml.so", - "debugSoLink": "https://github.com/RedBrumbler/Quest-BSML/releases/download/v0.4.41/debug_libbsml.so", + "soLink": "https://github.com/RedBrumbler/Quest-BSML/releases/download/v0.4.43/libbsml.so", + "debugSoLink": "https://github.com/RedBrumbler/Quest-BSML/releases/download/v0.4.43/debug_libbsml.so", "overrideSoName": "libbsml.so", - "modLink": "https://github.com/RedBrumbler/Quest-BSML/releases/download/v0.4.41/BSML.qmod", - "branchName": "version/v0_4_41", + "modLink": "https://github.com/RedBrumbler/Quest-BSML/releases/download/v0.4.43/BSML.qmod", + "branchName": "version/v0_4_43", "cmake": true } }, - "version": "0.4.41" + "version": "0.4.43" }, { "dependency": { "id": "custom-types", - "versionRange": "=0.17.8", + "versionRange": "=0.17.10", "additionalData": { - "soLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.17.8/libcustom-types.so", - "debugSoLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.17.8/debug_libcustom-types.so", + "soLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.17.10/libcustom-types.so", + "debugSoLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.17.10/debug_libcustom-types.so", "overrideSoName": "libcustom-types.so", - "modLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.17.8/CustomTypes.qmod", - "branchName": "version/v0_17_8", + "modLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.17.10/CustomTypes.qmod", + "branchName": "version/v0_17_10", "compileOptions": { "cppFlags": [ "-Wno-invalid-offsetof" @@ -168,7 +168,7 @@ "cmake": true } }, - "version": "0.17.8" + "version": "0.17.10" }, { "dependency": { @@ -190,10 +190,10 @@ { "dependency": { "id": "bs-cordl", - "versionRange": "=3500.0.0", + "versionRange": "=3700.0.0", "additionalData": { "headersOnly": true, - "branchName": "version/v3500_0_0", + "branchName": "version/v3700_0_0", "compileOptions": { "includePaths": [ "include" @@ -208,7 +208,7 @@ } } }, - "version": "3500.0.0" + "version": "3700.0.0" }, { "dependency": { @@ -225,29 +225,30 @@ { "dependency": { "id": "songcore", - "versionRange": "=1.1.9", + "versionRange": "=1.1.15", "additionalData": { - "soLink": "https://github.com/raineio/Quest-SongCore/releases/download/v1.1.9/libsongcore.so", - "debugSoLink": "https://github.com/raineio/Quest-SongCore/releases/download/v1.1.9/debug_libsongcore.so", + "soLink": "https://github.com/raineio/Quest-SongCore/releases/download/v1.1.15/libsongcore.so", + "debugSoLink": "https://github.com/raineio/Quest-SongCore/releases/download/v1.1.15/debug_libsongcore.so", "overrideSoName": "libsongcore.so", - "modLink": "https://github.com/raineio/Quest-SongCore/releases/download/v1.1.9/SongCore.qmod", - "branchName": "version/v1_1_9" + "modLink": "https://github.com/raineio/Quest-SongCore/releases/download/v1.1.15/SongCore.qmod", + "branchName": "version/v1_1_15", + "cmake": true } }, - "version": "1.1.9" + "version": "1.1.15" }, { "dependency": { "id": "beatsaber-hook", - "versionRange": "=5.1.7", + "versionRange": "=5.1.9", "additionalData": { - "soLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v5.1.7/libbeatsaber-hook_5_1_7.so", - "debugSoLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v5.1.7/debug_libbeatsaber-hook_5_1_7.so", - "branchName": "version/v5_1_7", + "soLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v5.1.9/libbeatsaber-hook_5_1_9.so", + "debugSoLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v5.1.9/debug_libbeatsaber-hook_5_1_9.so", + "branchName": "version/v5_1_9", "cmake": true } }, - "version": "5.1.7" + "version": "5.1.9" }, { "dependency": { diff --git a/shared/CP_SDK/ChatPlexSDK.hpp b/shared/CP_SDK/ChatPlexSDK.hpp index 2e5e66d..c616b61 100644 --- a/shared/CP_SDK/ChatPlexSDK.hpp +++ b/shared/CP_SDK/ChatPlexSDK.hpp @@ -29,7 +29,6 @@ namespace CP_SDK { CP_SDK_NO_DEF_CTORS(ChatPlexSDK); public: - /// Logger instance static Logging::ILogger* Logger() { return m_Logger; } static std::u16string_view ProductName() { return m_ProductName; } @@ -49,38 +48,44 @@ namespace CP_SDK { /// @param p_BasePath Base path for file storage /// @param p_RenderPipeline Rendering pipeline static void Configure(Logging::ILogger* p_Logger, std::u16string_view p_ProductName, std::string_view p_BasePath, ERenderPipeline p_RenderPipeline); - /// When the assembly is loaded + /// @brief When the assembly is loaded static void OnAssemblyLoaded(); - /// On assembly exit + /// @brief On assembly exit static void OnAssemblyExit(); public: - /// When unity is ready + /// @brief When unity is ready static void OnUnityReady(); - /// When unity is exiting + /// @brief When unity is exiting static void OnUnityExit(); public: - /// Register a module - /// @p_Module: Module instance + /// @brief Register a module + /// @param p_Module: Module instance static void RegisterModule(IModuleBase* p_Module); - /// Init all the available modules + /// @brief Init all the available modules static void InitModules(); - /// Stop modules + /// @brief Stop modules static void StopModules(); - /// Get modules + /// @brief Get modules + /// @return Const reference of a list containing the modules static const std::vector & GetModules() { return m_Modules; } public: - /// On generic menu scene + /// @brief Open an URL on the system + /// @param p_URL URL to open + static void OpenURL(std::u16string_view p_URL); + + public: + /// @brief On generic menu scene static void Fire_OnGenericMenuSceneLoaded(); - /// On generic menu scene + /// @brief On generic menu scene static void Fire_OnGenericMenuScene(); - /// On generic play scene + /// @brief On generic play scene static void Fire_OnGenericPlayingScene(); private: - /// Install WEBP codecs + /// @brief Install WEBP codecs static void InstallWEBPCodecs(); private: diff --git a/shared/CP_SDK/UI/UISystem.hpp b/shared/CP_SDK/UI/UISystem.hpp index bb909e4..7ad66ba 100644 --- a/shared/CP_SDK/UI/UISystem.hpp +++ b/shared/CP_SDK/UI/UISystem.hpp @@ -105,6 +105,7 @@ namespace CP_SDK::UI { static _u::Color TooltipBGColor; static _u::Color TextColor; + static _u::Color TextColorDisabled; public: static SafePtr<_u::Type> Override_UnityComponent_Image; diff --git a/shared/CP_SDK/Unity/Operators.hpp b/shared/CP_SDK/Unity/Operators.hpp index ce524ca..17e4f42 100644 --- a/shared/CP_SDK/Unity/Operators.hpp +++ b/shared/CP_SDK/Unity/Operators.hpp @@ -6,6 +6,9 @@ #include #include #include +#include + +#include constexpr System::TimeSpan operator- (const System::DateTime& p_A, const System::DateTime& p_B) { @@ -122,4 +125,34 @@ constexpr UnityEngine::Quaternion operator* (const UnityEngine::Quaternion& p_A, constexpr UnityEngine::Vector3 operator* (const UnityEngine::Quaternion& p_A, const UnityEngine::Vector3& p_B) { return UnityEngine::Quaternion::op_Multiply(p_A, p_B); +} + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// + +static UnityEngine::Vector3 Vector3RotateTowards(UnityEngine::Vector3 p_Current, UnityEngine::Vector3 p_Target, float p_MaxRadiansDelta, float p_MaxMagnitudeDelta) +{ + const float Deg2Rad = static_cast(std::numbers::pi) / 180.0f; + const float Rad2Deg = 57.29578f; + + auto l_FromDirection = p_Current.get_normalized(); + auto l_ToDirection = p_Target.get_normalized(); + auto l_AngleRadians = UnityEngine::Mathf::Acos(UnityEngine::Vector3::Dot(l_FromDirection, l_ToDirection)); + + if (l_AngleRadians < std::numeric_limits::epsilon()) + return p_Current; + + auto l_ClampedAngleRad = UnityEngine::Mathf::Min(p_MaxRadiansDelta, l_AngleRadians); + auto l_Axis = UnityEngine::Vector3::Cross(l_FromDirection, l_ToDirection); + + if (l_Axis.get_sqrMagnitude() < std::numeric_limits::epsilon()) + l_Axis = UnityEngine::Vector3::get_up(); + + auto l_Rotation = UnityEngine::Quaternion::AngleAxis(l_ClampedAngleRad * Rad2Deg, l_Axis); + auto l_RotatedVector = l_Rotation * l_FromDirection; + auto l_CurrentMagnitude = p_Current.get_magnitude(); + auto l_TargetMagnitude = p_Target.get_magnitude(); + auto l_ClampedMagnitude = UnityEngine::Mathf::MoveTowards(l_CurrentMagnitude, l_TargetMagnitude, p_MaxMagnitudeDelta); + + return l_RotatedVector * l_ClampedMagnitude; } \ No newline at end of file diff --git a/shared/CP_SDK/Utils/MonoPtr.hpp b/shared/CP_SDK/Utils/MonoPtr.hpp index ed29807..5e08201 100644 --- a/shared/CP_SDK/Utils/MonoPtr.hpp +++ b/shared/CP_SDK/Utils/MonoPtr.hpp @@ -41,7 +41,7 @@ namespace CP_SDK::Utils { if constexpr (std::is_assignable_v) { auto l_UObject = reinterpret_cast(m_Wrapper->Ptr); - if (l_IsDead || !l_UObject->m_CachedPtr) + if (l_IsDead || !l_UObject->___m_CachedPtr.m_value) l_IsDead = true; } @@ -132,7 +132,7 @@ namespace CP_SDK::Utils { return false; auto l_UObject = reinterpret_cast(p_Ptr); - if (!l_UObject->m_CachedPtr) + if (!l_UObject->___m_CachedPtr.m_value) return false; return true; diff --git a/shared/CP_SDK_BS/Game/Levels.hpp b/shared/CP_SDK_BS/Game/Levels.hpp index 9ba6dcd..3a33b4c 100644 --- a/shared/CP_SDK_BS/Game/Levels.hpp +++ b/shared/CP_SDK_BS/Game/Levels.hpp @@ -67,6 +67,10 @@ namespace CP_SDK_BS::Game { /// @param p_Capability Capability name /// @return True or false static bool HasMappingCapability(std::u16string_view p_Capability); + /// @brief Sanatrize a mapping capability + /// @param p_Capability Capability name + /// @return Sanatized mapping capability + static std::u16string SanatizeMappingCapability(std::u16string_view p_Capability); public: /// @brief Sanitize a level ID for case matching diff --git a/src/CP_SDK/ChatPlexSDK.cpp b/src/CP_SDK/ChatPlexSDK.cpp index c623f94..6baed2f 100644 --- a/src/CP_SDK/ChatPlexSDK.cpp +++ b/src/CP_SDK/ChatPlexSDK.cpp @@ -10,6 +10,8 @@ #include "CP_SDK/Unity/MTThreadInvoker.hpp" #include "CP_SDK/ModuleBase.hpp" +#include + namespace CP_SDK { Logging::ILogger* ChatPlexSDK::m_Logger = nullptr; @@ -44,7 +46,7 @@ namespace CP_SDK { m_NetworkUserAgent = std::u16string(u"ChatPlexSDK_") + m_ProductName; m_RenderPipeline = p_RenderPipeline; } - /// When the assembly is loaded + /// @brief When the assembly is loaded void ChatPlexSDK::OnAssemblyLoaded() { InstallWEBPCodecs(); @@ -52,7 +54,7 @@ namespace CP_SDK { /// Init config Chat::Service::Init(); } - /// On assembly exit + /// @brief On assembly exit void ChatPlexSDK::OnAssemblyExit() { try @@ -69,7 +71,7 @@ namespace CP_SDK { //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// - /// When unity is ready + /// @brief When unity is ready void ChatPlexSDK::OnUnityReady() { try @@ -92,7 +94,7 @@ namespace CP_SDK { m_Logger->Error(p_Exception); } } - /// When unity is exiting + /// @brief When unity is exiting void ChatPlexSDK::OnUnityExit() { try @@ -122,13 +124,13 @@ namespace CP_SDK { //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// - /// Register a module - /// @p_Module: Module instance + /// @brief Register a module + /// @param p_Module Module instance void ChatPlexSDK::RegisterModule(IModuleBase * p_Module) { m_Modules.push_back(p_Module); } - /// Init all the available modules + /// @brief Init all the available modules void ChatPlexSDK::InitModules() { try @@ -153,7 +155,7 @@ namespace CP_SDK { m_Logger->Error(p_Exception); } } - /// Stop modules + /// @brief Stop modules void ChatPlexSDK::StopModules() { for (auto l_Module : m_Modules) @@ -173,7 +175,19 @@ namespace CP_SDK { //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// - /// On generic menu scene + /// @brief Open an URL on the system + /// @param p_URL URL to open + void ChatPlexSDK::OpenURL(std::u16string_view p_URL) + { + static auto s_Application_OpenURL = il2cpp_utils::resolve_icall("UnityEngine.Application::OpenURL"); + if (s_Application_OpenURL) + s_Application_OpenURL(p_URL); + } + + //////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// + + /// @brief On generic menu scene void ChatPlexSDK::Fire_OnGenericMenuSceneLoaded() { try @@ -204,7 +218,7 @@ namespace CP_SDK { m_Logger->Error(l_Exception); } } - /// On generic menu scene + /// @brief On generic menu scene void ChatPlexSDK::Fire_OnGenericMenuScene() { m_ActiveGenericScene = EGenericScene::Menu; @@ -221,7 +235,7 @@ namespace CP_SDK { m_Logger->Error(l_Exception); } } - /// On generic play scene + /// @brief On generic play scene void ChatPlexSDK::Fire_OnGenericPlayingScene() { m_ActiveGenericScene = EGenericScene::Playing; @@ -240,7 +254,7 @@ namespace CP_SDK { //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// - /// Install WEBP codecs + /// @brief Install WEBP codecs void ChatPlexSDK::InstallWEBPCodecs() { /// Codecs are built-in sources diff --git a/src/CP_SDK/UI/DefaultComponents/DefaultCDropdown.cpp b/src/CP_SDK/UI/DefaultComponents/DefaultCDropdown.cpp index 8855826..8b5c883 100644 --- a/src/CP_SDK/UI/DefaultComponents/DefaultCDropdown.cpp +++ b/src/CP_SDK/UI/DefaultComponents/DefaultCDropdown.cpp @@ -146,7 +146,7 @@ namespace CP_SDK::UI::DefaultComponents { { m_Button->set_interactable(p_Interactable); - m_ValueText->SetColor(p_Interactable ? UISystem::PrimaryColor : UISystem::SecondaryColor); + m_ValueText->SetColor(p_Interactable ? UISystem::TextColor : UISystem::TextColorDisabled); m_Icon->SetInteractable(p_Interactable); } /// @brief Set available options diff --git a/src/CP_SDK/UI/DefaultComponents/DefaultCSlider.cpp b/src/CP_SDK/UI/DefaultComponents/DefaultCSlider.cpp index 82d2eab..3fedd0b 100644 --- a/src/CP_SDK/UI/DefaultComponents/DefaultCSlider.cpp +++ b/src/CP_SDK/UI/DefaultComponents/DefaultCSlider.cpp @@ -424,10 +424,10 @@ namespace CP_SDK::UI::DefaultComponents { void DefaultCSlider::UpdateDrag(PointerEventData* p_EventData) { Vector2 l_LocalPoint; - if (p_EventData->button != PointerEventData::InputButton::Left + if (p_EventData->get_button() != PointerEventData::InputButton::Left || !m_SlidingArea - || p_EventData->hovered->get_Count() == 0 - || !RectTransformUtility::ScreenPointToLocalPointInRectangle(m_SlidingArea.Ptr(), p_EventData->position, p_EventData->get_pressEventCamera(), /*OUT*/l_LocalPoint) + || p_EventData->___hovered->get_Count() == 0 + || !RectTransformUtility::ScreenPointToLocalPointInRectangle(m_SlidingArea.Ptr(), p_EventData->get_position(), p_EventData->get_pressEventCamera(), /*OUT*/l_LocalPoint) || std::isnan(l_LocalPoint.x) || std::isnan(l_LocalPoint.y)) return; @@ -552,6 +552,8 @@ namespace CP_SDK::UI::DefaultComponents { m_IncButton->SetInteractable(l_IsInteractable); m_BG->set_color(ColorU::WithAlpha(l_IsInteractable ? m_OnColor : m_OffColor, l_IsInteractable ? 110.0f / 255.0f : 50.0f / 255.0f)); } + + m_ValueText->SetColor(l_IsInteractable ? UISystem::TextColor : UISystem::TextColorDisabled); } /// @brief Update visuals void DefaultCSlider::UpdateVisuals() diff --git a/src/CP_SDK/UI/DefaultComponents/DefaultCTextInput.cpp b/src/CP_SDK/UI/DefaultComponents/DefaultCTextInput.cpp index bb08752..c9c4bee 100644 --- a/src/CP_SDK/UI/DefaultComponents/DefaultCTextInput.cpp +++ b/src/CP_SDK/UI/DefaultComponents/DefaultCTextInput.cpp @@ -140,7 +140,7 @@ namespace CP_SDK::UI::DefaultComponents { { m_Button->set_interactable(p_Interactable); - m_ValueText->SetColor(ColorU::WithAlpha(Color::get_white(), p_Interactable ? 1.0f : 0.5f)); + m_ValueText->SetColor(p_Interactable ? UISystem::TextColor : UISystem::TextColorDisabled); m_Icon->SetColor(ColorU::WithAlpha(Color::get_white(), p_Interactable ? 1.0f : 0.5f)); } /// @brief Set is password diff --git a/src/CP_SDK/UI/UISystem.cpp b/src/CP_SDK/UI/UISystem.cpp index 19c8e03..e56fc2a 100644 --- a/src/CP_SDK/UI/UISystem.cpp +++ b/src/CP_SDK/UI/UISystem.cpp @@ -105,6 +105,7 @@ namespace CP_SDK::UI { Color UISystem::TooltipBGColor = ColorU::WithAlpha("#3A3A3B", 0.9875f); Color UISystem::TextColor = ColorU::WithAlpha("#FFFFFF", 1.0000f); + Color UISystem::TextColorDisabled = Color::op_Multiply(TextColor, 0.75f); //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// diff --git a/src/CP_SDK/UI/Views/MainLeftView.cpp b/src/CP_SDK/UI/Views/MainLeftView.cpp index b15559d..5f33e14 100644 --- a/src/CP_SDK/UI/Views/MainLeftView.cpp +++ b/src/CP_SDK/UI/Views/MainLeftView.cpp @@ -2,8 +2,6 @@ #include "CP_SDK/UI/FlowCoordinators/MainFlowCoordinator.hpp" #include "CP_SDK/Unity/SpriteU.hpp" -#include - using namespace CP_SDK::XUI; using namespace UnityEngine; @@ -59,13 +57,13 @@ namespace CP_SDK::UI::Views { void MainLeftView::OnDocumentationButton() { ShowMessageModal(u"URL opened in your web browser."); - Application::OpenURL("https://github.com/hardcpp/BeatSaberPlus/wiki"); + ChatPlexSDK::OpenURL(u"https://github.com/hardcpp/BeatSaberPlus/wiki"); } /// @brief Go to discord void MainLeftView::OnDiscordButton() { ShowMessageModal(u"URL opened in your web browser."); - Application::OpenURL("https://discord.chatplex.org"); + ChatPlexSDK::OpenURL(u"https://discord.chatplex.org"); } //////////////////////////////////////////////////////////////////////////// @@ -75,7 +73,7 @@ namespace CP_SDK::UI::Views { void MainLeftView::OnDonateButton() { ShowMessageModal(u"URL opened in your web browser."); - Application::OpenURL("https://donate.chatplex.org"); + ChatPlexSDK::OpenURL(u"https://donate.chatplex.org"); } } ///< namespace CP_SDK::UI::Views \ No newline at end of file diff --git a/src/CP_SDK/UI/Views/SettingsMainView.cpp b/src/CP_SDK/UI/Views/SettingsMainView.cpp index 795e5d1..2460d59 100644 --- a/src/CP_SDK/UI/Views/SettingsMainView.cpp +++ b/src/CP_SDK/UI/Views/SettingsMainView.cpp @@ -5,8 +5,6 @@ #include "CP_SDK/CPConfig.hpp" #include "CP_SDK/ModuleBase.hpp" -#include - using namespace CP_SDK::XUI; using namespace UnityEngine; @@ -163,7 +161,7 @@ namespace CP_SDK::UI::Views { void SettingsMainView::OnDocumentationButton(IModuleBase* p_Module) { ShowMessageModal(u"URL opened in your web browser."); - Application::OpenURL(p_Module->DocumentationURL()); + ChatPlexSDK::OpenURL(p_Module->DocumentationURL()); } //////////////////////////////////////////////////////////////////////////// diff --git a/src/CP_SDK/Unity/FontManager.cpp b/src/CP_SDK/Unity/FontManager.cpp index 0b06c0a..c331d0a 100644 --- a/src/CP_SDK/Unity/FontManager.cpp +++ b/src/CP_SDK/Unity/FontManager.cpp @@ -9,6 +9,41 @@ using namespace TMPro; using namespace UnityEngine; +/// Temp fix from BSML until libunity is properly unstripped + #include + #include + #include + #include + #include + + bool TryGetSoloButton(UnityEngine::UI::Button*& p_Button) + { + static SafePtrUnity g_SoloButton; + if(!g_SoloButton) + { + auto l_MainMenuViewController = UnityEngine::Resources::FindObjectsOfTypeAll()->First(); + g_SoloButton = l_MainMenuViewController->____soloButton; + } + + p_Button = g_SoloButton.ptr(); + return g_SoloButton; + } + bool TryGetUITextTemplate(TMPro::TextMeshProUGUI*& p_TextMeshProUGUI) + { + UnityEngine::UI::Button* l_SoloButton; + if (!TryGetSoloButton(l_SoloButton)) + { + p_TextMeshProUGUI = nullptr; + return false; + } + auto l_Transform = l_SoloButton->get_transform(); + auto l_Text = l_Transform->Find("Text"); + + p_TextMeshProUGUI = l_Text->GetComponent(); + return p_TextMeshProUGUI; + } +/// End of temp fix + namespace CP_SDK::Unity { bool FontManager::m_IsInitialized = false; @@ -33,6 +68,8 @@ namespace CP_SDK::Unity { { ChatPlexSDK::Logger()->Error(u"[CP_SDK.Unity][FontManager.Init] Loading font asset bundle..."); + /// Temp disable until libunity is properly unstripped + /* static auto s_LoadFromMemory = reinterpret_cast(il2cpp_functions::resolve_icall("UnityEngine.AssetBundle::LoadFromMemory_Internal")); m_AssetBundle = s_LoadFromMemory(Assets::QuestFonts_bundle, 0); @@ -43,6 +80,7 @@ namespace CP_SDK::Unity { } Object::DontDestroyOnLoad(m_AssetBundle.Ptr()); + */ ChatPlexSDK::Logger()->Info(u"[CP_SDK.Unity][FontManager.Init] Font asset bundle loaded"); m_IsInitialized = true; @@ -59,19 +97,31 @@ namespace CP_SDK::Unity { if (!m_MainFont) { + /// Temp fix from BSML until libunity is properly unstripped + TMPro::TextMeshProUGUI* p_TextMeshProUGUI; + if (TryGetUITextTemplate(p_TextMeshProUGUI)) + m_MainFont = p_TextMeshProUGUI->get_font(); + /// End of temp fix + + /// Temp disable until libunity is properly unstripped + /* if (!m_BundleMainFont) m_BundleMainFont = m_AssetBundle->LoadAsset("[CP_SDK]segoeui SDF Main"); m_MainFont = m_BundleMainFont; + */ if (m_TMPFontAssetSetup.IsValid()) m_MainFont = m_TMPFontAssetSetup(m_MainFont.Ptr()); + /// Temp disable until libunity is properly unstripped + /* m_MainFont->___normalStyle = 0.5f; m_MainFont->___normalSpacingOffset = -1.0f; m_MainFont->___boldStyle = 2.0f; m_MainFont->___boldSpacing = 2.0f; m_MainFont->___italicStyle = 15; + */ } return m_MainFont.Ptr(); diff --git a/src/CP_SDK_BS/Game/LevelCompletionData.cpp b/src/CP_SDK_BS/Game/LevelCompletionData.cpp index ac22363..17a3442 100644 --- a/src/CP_SDK_BS/Game/LevelCompletionData.cpp +++ b/src/CP_SDK_BS/Game/LevelCompletionData.cpp @@ -14,11 +14,11 @@ namespace CP_SDK_BS::Game { return false; std::vector l_Requirements; - if (Levels::TryGetCustomRequirementsFor(Data->beatmapLevel, Data->beatmapKey.beatmapCharacteristic, Data->beatmapKey.difficulty, &l_Requirements)) + if (Levels::TryGetCustomRequirementsFor(Data->___beatmapLevel, Data->___beatmapKey.beatmapCharacteristic, Data->___beatmapKey.difficulty, &l_Requirements)) { for (auto& l_Current : l_Requirements) { - if (_v::U16EqualsToCaseInsensitive(l_Current, u"Noodle Extensions")) + if (CP_SDK::Utils::U16EqualsToCaseInsensitive(Levels::SanatizeMappingCapability(l_Current), u"NoodleExtensions")) continue; return true; @@ -33,11 +33,11 @@ namespace CP_SDK_BS::Game { return false; std::vector l_Requirements; - if (Levels::TryGetCustomRequirementsFor(Data->beatmapLevel, Data->beatmapKey.beatmapCharacteristic, Data->beatmapKey.difficulty, &l_Requirements)) + if (Levels::TryGetCustomRequirementsFor(Data->___beatmapLevel, Data->___beatmapKey.beatmapCharacteristic, Data->___beatmapKey.difficulty, &l_Requirements)) { for (auto& l_Current : l_Requirements) { - if (_v::U16EqualsToCaseInsensitive(l_Current, u"Chroma")) + if (CP_SDK::Utils::U16EqualsToCaseInsensitive(Levels::SanatizeMappingCapability(l_Current), u"Chroma")) continue; return true; diff --git a/src/CP_SDK_BS/Game/LevelData.cpp b/src/CP_SDK_BS/Game/LevelData.cpp index 489172f..3b3350a 100644 --- a/src/CP_SDK_BS/Game/LevelData.cpp +++ b/src/CP_SDK_BS/Game/LevelData.cpp @@ -10,22 +10,22 @@ namespace CP_SDK_BS::Game { bool LevelData::HasRotations() { - if (!Data || !Data->transformedBeatmapData) + if (!Data || !Data->get_transformedBeatmapData()) return false; - return Data->transformedBeatmapData->get_spawnRotationEventsCount() > 0; + return Data->get_transformedBeatmapData()->get_spawnRotationEventsCount() > 0; } bool LevelData::IsNoodle() { - if (!Data || !Data->beatmapLevel) + if (!Data || !Data->___beatmapLevel) return false; std::vector l_Requirements; - if (Levels::TryGetCustomRequirementsFor(Data->beatmapLevel, Data->beatmapKey.beatmapCharacteristic, Data->beatmapKey.difficulty, &l_Requirements)) + if (Levels::TryGetCustomRequirementsFor(Data->___beatmapLevel, Data->___beatmapKey.beatmapCharacteristic, Data->___beatmapKey.difficulty, &l_Requirements)) { for (auto& l_Current : l_Requirements) { - if (CP_SDK::Utils::U16EqualsToCaseInsensitive(l_Current, u"Noodle Extensions")) + if (CP_SDK::Utils::U16EqualsToCaseInsensitive(Levels::SanatizeMappingCapability(l_Current), u"NoodleExtensions")) continue; return true; @@ -40,11 +40,11 @@ namespace CP_SDK_BS::Game { return false; std::vector l_Requirements; - if (Levels::TryGetCustomRequirementsFor(Data->beatmapLevel, Data->beatmapKey.beatmapCharacteristic, Data->beatmapKey.difficulty, &l_Requirements)) + if (Levels::TryGetCustomRequirementsFor(Data->___beatmapLevel, Data->___beatmapKey.beatmapCharacteristic, Data->___beatmapKey.difficulty, &l_Requirements)) { for (auto& l_Current : l_Requirements) { - if (CP_SDK::Utils::U16EqualsToCaseInsensitive(l_Current, u"Chroma")) + if (CP_SDK::Utils::U16EqualsToCaseInsensitive(Levels::SanatizeMappingCapability(l_Current), u"Chroma")) continue; return true; diff --git a/src/CP_SDK_BS/Game/Levels.cpp b/src/CP_SDK_BS/Game/Levels.cpp index 4f7882a..84e2d0f 100644 --- a/src/CP_SDK_BS/Game/Levels.cpp +++ b/src/CP_SDK_BS/Game/Levels.cpp @@ -122,9 +122,12 @@ namespace CP_SDK_BS::Game { bool Levels::HasMappingCapability(std::u16string_view p_Capability) { auto l_Capabilities = SongCore::API::Capabilities::GetRegisteredCapabilities(); + auto l_Sanatized = SanatizeMappingCapability(p_Capability); + CP_SDK::ChatPlexSDK::Logger()->Error(u"HasMappingCapability: " + l_Sanatized); for (auto& l_Capability : l_Capabilities) { - if (!CP_SDK::Utils::U16EqualsToCaseInsensitive(p_Capability, CP_SDK::Utils::StrToU16Str(l_Capability))) + CP_SDK::ChatPlexSDK::Logger()->Error(u"Mapping cap: " + CP_SDK::Utils::StrToU16Str(l_Capability)); + if (!CP_SDK::Utils::U16EqualsToCaseInsensitive(l_Sanatized, CP_SDK::Utils::StrToU16Str(l_Capability))) continue; return true; @@ -132,6 +135,24 @@ namespace CP_SDK_BS::Game { return false; } + /// @brief Sanatrize a mapping capability + /// @param p_Capability Capability name + /// @return Sanatized mapping capability + std::u16string Levels::SanatizeMappingCapability(std::u16string_view p_Capability) + { + std::u16string l_Result; + + l_Result.reserve(p_Capability.size()); + for (auto l_Char : p_Capability) + { + if (l_Char == u' ') + continue; + + l_Result.push_back(std::towlower(l_Char)); + } + + return l_Result; + } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// @@ -591,21 +612,23 @@ namespace CP_SDK_BS::Game { return false; } - auto l_StandardLevelInfoSaveData = il2cpp_utils::try_cast(l_CustomLevel->get_standardLevelInfoSaveData()).value_or(nullptr); - if (!l_StandardLevelInfoSaveData) + auto l_CustomSaveDataInfoWrapper = l_CustomLevel->get_CustomSaveDataInfo(); + if (!l_CustomSaveDataInfoWrapper) { CP_SDK::ChatPlexSDK::Logger()->Error(u"[CP_SDK_BS.Game][Levels.TryGetCustomRequirementsFor] Failed to retrieve custom data level for id: " + p_BeatmapLevel->___levelID); return false; } - auto l_CharacteristicAndDifficultyWrapper = l_StandardLevelInfoSaveData->TryGetCharacteristicAndDifficulty(p_BeatmapCharacteristicSO->____serializedName, p_BeatmapDifficulty); + auto& l_CustomSaveDataInfo = l_CustomSaveDataInfoWrapper->get(); + + auto l_CharacteristicAndDifficultyWrapper = l_CustomSaveDataInfo.TryGetCharacteristicAndDifficulty(p_BeatmapCharacteristicSO->____serializedName, p_BeatmapDifficulty); if (!l_CharacteristicAndDifficultyWrapper) { CP_SDK::ChatPlexSDK::Logger()->Error(u"[CP_SDK_BS.Game][Levels.TryGetCustomRequirementsFor] Failed to retrieve custom data level for id: " + p_BeatmapLevel->___levelID); return false; } - auto l_CharacteristicAndDifficulty = l_CharacteristicAndDifficultyWrapper.value().get(); + const auto& l_CharacteristicAndDifficulty = l_CharacteristicAndDifficultyWrapper.value().get(); p_CustomRequirements->reserve(l_CharacteristicAndDifficulty.requirements.size()); for (auto& l_Requirement : l_CharacteristicAndDifficulty.requirements) diff --git a/src/CP_SDK_BS/UI/LevelDetail.cpp b/src/CP_SDK_BS/UI/LevelDetail.cpp index 1770aca..5a9a778 100644 --- a/src/CP_SDK_BS/UI/LevelDetail.cpp +++ b/src/CP_SDK_BS/UI/LevelDetail.cpp @@ -5,6 +5,7 @@ #include "CP_SDK/UI/UISystem.hpp" #include "CP_SDK/Unity/SpriteU.hpp" #include "CP_SDK/Unity/Operators.hpp" +#include "CP_SDK/Unity/MTThreadInvoker.hpp" #include "assets.hpp" #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -38,8 +40,6 @@ #include "songcore/shared/SongLoader/CustomBeatmapLevel.hpp" -#include "CP_SDK_BS/UI/DefaultComponentsOverrides/Subs/SubFloatingPanelMover.hpp" - using namespace GlobalNamespace; using namespace TMPro; using namespace UnityEngine; @@ -66,7 +66,11 @@ namespace CP_SDK_BS::UI { return; m_SongDetailViewTemplate = GameObject::Instantiate(l_Result->get_gameObject()); - m_SongDetailViewTemplate->set_name(u"CP_SDK_BS_StandardLevelDetailView_Template"); + m_SongDetailViewTemplate->set_name(u"CP_SDK_BS_SongDetailViewTemplate"); + + auto l_LevelBarBig = m_SongDetailViewTemplate->get_transform()->Find(u"LevelBarBig"); + if (l_LevelBarBig) + l_LevelBarBig->get_gameObject()->set_name("CP_SDK_BS_LevelBarBig"); try { @@ -75,7 +79,13 @@ namespace CP_SDK_BS::UI { { auto l_Loader = BeatmapLevelLoader::New_ctor(nullptr, MockBeatmapDataAssetFileModel::New_ctor()->i___GlobalNamespace__IBeatmapDataAssetFileModel(), nullptr, BeatmapLevelLoader::InitData::New_ctor(0)); auto l_Packs = System::Collections::Generic::List_1<::UnityW>::New_ctor(); - l_Component->____beatmapLevelsModel = BeatmapLevelsModel::New_ctor(nullptr, l_Loader->i___GlobalNamespace__IBeatmapLevelLoader(), l_Packs->i___System__Collections__Generic__IEnumerable_1_T_()); + l_Component->____beatmapLevelsModel = BeatmapLevelsModel::New_ctor( + nullptr, + l_Loader->i___GlobalNamespace__IBeatmapLevelLoader(), + nullptr, + nullptr, + l_Packs->i___System__Collections__Generic__IEnumerable_1_T_() + ); GameObject::DestroyImmediate(l_Component); } @@ -286,7 +296,7 @@ namespace CP_SDK_BS::UI { m_DifficultiesSegmentedControllerClone = m_GameObject->get_transform()->Find(u"BeatmapDifficulty")->GetComponentInChildren(); m_SongDiffSegmentedControl = HMUITextSegmentedControl::Create(m_DifficultiesSegmentedControllerClone->get_transform().try_cast().value_or(nullptr), true); - auto l_LevelBarBig = m_GameObject->get_transform()->Find(u"LevelBarBig"); + auto l_LevelBarBig = m_GameObject->get_transform()->Find(u"CP_SDK_BS_LevelBarBig"); m_SongNameText = l_LevelBarBig->GetComponentsInChildren()->First([](auto x) { return x->get_gameObject()->get_name() == u"SongNameText"; }); m_AuthorNameText = l_LevelBarBig->GetComponentsInChildren()->First([](auto x) { return x->get_gameObject()->get_name() == u"AuthorNameText"; }); @@ -311,7 +321,7 @@ namespace CP_SDK_BS::UI { m_SongBombsText = l_BeatmapParamsPanel->GetComponentsInChildren()->First([](auto x) { return x->get_gameObject()->get_transform()->get_parent()->get_name() == u"BombsCount";}); auto l_SizeDelta = m_SongNPSText->get_transform()->get_parent()->get_transform().try_cast().value_or(nullptr)->get_sizeDelta(); - l_SizeDelta.y *= 2; + l_SizeDelta.y *= 2.0f; m_SongNPSText->get_transform()->get_parent()->get_gameObject()->AddComponent()->set_padding(RectOffset::New_ctor(0, 0, 0, 3)); m_SongNPSText->get_transform()->get_parent()->get_gameObject()->AddComponent(); @@ -323,7 +333,7 @@ namespace CP_SDK_BS::UI { m_SongObstaclesText->get_transform()->get_parent()->get_gameObject()->AddComponent()->set_padding(RectOffset::New_ctor(0, 0, 0, 3)); m_SongObstaclesText->get_transform()->get_parent()->get_gameObject()->AddComponent(); - m_SongObstaclesText->get_transform()->get_parent()->get_transform()->get_transform()->get_parent()->get_transform().try_cast().value_or(nullptr)->set_sizeDelta(l_SizeDelta); + m_SongObstaclesText->get_transform()->get_parent()->get_transform().try_cast().value_or(nullptr)->set_sizeDelta(l_SizeDelta); m_SongBombsText->get_transform()->get_parent()->get_gameObject()->AddComponent()->set_padding(RectOffset::New_ctor(0, 0, 0, 3)); m_SongBombsText->get_transform()->get_parent()->get_gameObject()->AddComponent(); @@ -418,11 +428,11 @@ namespace CP_SDK_BS::UI { /// Display mode Characteristic(HMUI::IconSegmentedControl::DataItem::New_ctor(p_Characteristic->____icon, BGLib::Polyglot::Localization::Get(p_Characteristic->____descriptionLocalizationKey))); - auto l_DifficultyBeatmap = p_BeatMap->GetDifficultyBeatmapData(p_Characteristic, p_Difficulty); - /// Display difficulty Difficulty(Game::Levels::BeatmapDifficultySerializedNameToDifficultyName(BeatmapDifficultySerializedMethods::SerializedName(p_Difficulty))); + auto l_DifficultyBeatmap = p_BeatMap->GetDifficultyBeatmapData(p_Characteristic, p_Difficulty); + Name (p_BeatMap->___songName); AuthorNameText(u"Mapped by " + p_BeatMap->___allMappers->FirstOrDefault() + u""); Cover (p_Cover ? p_Cover : Game::Levels::GetDefaultPackCover()); @@ -430,10 +440,34 @@ namespace CP_SDK_BS::UI { BPM (p_BeatMap->___beatsPerMinute); NJS ((int)l_DifficultyBeatmap->___noteJumpMovementSpeed); Offset (l_DifficultyBeatmap->___noteJumpStartBeatOffset); - NPS (l_DifficultyBeatmap->___notesCount / p_BeatMap->___songDuration); - Notes (l_DifficultyBeatmap->___notesCount); - Obstacles (l_DifficultyBeatmap->___obstaclesCount); - Bombs (l_DifficultyBeatmap->___bombsCount); + + auto l_CustomBeatmapLevelCast = il2cpp_utils::try_cast(p_BeatMap); + if (l_CustomBeatmapLevelCast) + { + auto l_CustomBeatmapLevel = l_CustomBeatmapLevelCast.value(); + + NPS (-1); + Notes (-1); + Obstacles(-1); + Bombs (-1); + + BeatmapKey l_BeatmapKey; + if (Game::Levels::BeatmapLevel_TryGetBeatmapKey(p_BeatMap, p_Characteristic, p_Difficulty, &l_BeatmapKey)) + { + auto l_DifficultyBeatmap = BeatmapDataLoader::New_ctor()->LoadBasicBeatmapData(l_CustomBeatmapLevel->get_beatmapLevelData(), byref(l_BeatmapKey)); + NPS (static_cast(l_DifficultyBeatmap->get_cuttableNotesCount()) / std::max(static_cast(p_BeatMap->___songDuration), 1.0f)); + Notes (l_DifficultyBeatmap->get_cuttableNotesCount()); + Obstacles (l_DifficultyBeatmap->get_obstaclesCount()); + Bombs (l_DifficultyBeatmap->get_bombsCount()); + } + } + else + { + NPS (static_cast(l_DifficultyBeatmap->___notesCount) / std::max(static_cast(p_BeatMap->___songDuration), 1.0f)); + Notes (l_DifficultyBeatmap->___notesCount); + Obstacles (l_DifficultyBeatmap->___obstaclesCount); + Bombs (l_DifficultyBeatmap->___bombsCount); + } return true; } @@ -693,7 +727,7 @@ namespace CP_SDK_BS::UI { BPM (m_LocalBeatMap->___beatsPerMinute); NJS ((int)l_DifficultyBeatmap->___noteJumpMovementSpeed); Offset (l_DifficultyBeatmap->___noteJumpStartBeatOffset); - NPS (l_DifficultyBeatmap->___notesCount / m_LocalBeatMap->___songDuration); + NPS (static_cast(l_DifficultyBeatmap->___notesCount) / std::max(static_cast(m_LocalBeatMap->___songDuration), 1.0f)); Notes (l_DifficultyBeatmap->___notesCount); Obstacles (l_DifficultyBeatmap->___obstaclesCount); Bombs (l_DifficultyBeatmap->___bombsCount); @@ -725,8 +759,8 @@ namespace CP_SDK_BS::UI { SelectedBeatmapDifficulty = Game::Levels::BeatmapDifficultySerializedNameToBeatmapDifficulty(l_SelectedBeatmapCharacteristicDifficulty->difficulty); /// Display informations - Time ((double)m_BeatMap->metadata.value().duration); - NPS ((float)l_SelectedBeatmapCharacteristicDifficulty->nps); + Time (static_cast(m_BeatMap->metadata.value().duration)); + NPS (static_cast(l_SelectedBeatmapCharacteristicDifficulty->nps)); NJS ((int)l_SelectedBeatmapCharacteristicDifficulty->njs); Offset (l_SelectedBeatmapCharacteristicDifficulty->offset); Notes (l_SelectedBeatmapCharacteristicDifficulty->notes);