// Copyright Epic Games, Inc. All Rights Reserved. #include "LyraSettingsShared.h" #include "Framework/Application/SlateApplication.h" #include "Internationalization/Culture.h" #include "Kismet/GameplayStatics.h" #include "Misc/App.h" #include "Misc/ConfigCacheIni.h" #include "Player/LyraLocalPlayer.h" #include "Rendering/SlateRenderer.h" #include "SubtitleDisplaySubsystem.h" #include "EnhancedInputSubsystems.h" #include "UserSettings/EnhancedInputUserSettings.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(LyraSettingsShared) static FString SHARED_SETTINGS_SLOT_NAME = TEXT("SharedGameSettings"); namespace LyraSettingsSharedCVars { static float DefaultGamepadLeftStickInnerDeadZone = 0.25f; static FAutoConsoleVariableRef CVarGamepadLeftStickInnerDeadZone( TEXT("gpad.DefaultLeftStickInnerDeadZone"), DefaultGamepadLeftStickInnerDeadZone, TEXT("Gamepad left stick inner deadzone") ); static float DefaultGamepadRightStickInnerDeadZone = 0.25f; static FAutoConsoleVariableRef CVarGamepadRightStickInnerDeadZone( TEXT("gpad.DefaultRightStickInnerDeadZone"), DefaultGamepadRightStickInnerDeadZone, TEXT("Gamepad right stick inner deadzone") ); } ULyraSettingsShared::ULyraSettingsShared() { FInternationalization::Get().OnCultureChanged().AddUObject(this, &ThisClass::OnCultureChanged); GamepadMoveStickDeadZone = LyraSettingsSharedCVars::DefaultGamepadLeftStickInnerDeadZone; GamepadLookStickDeadZone = LyraSettingsSharedCVars::DefaultGamepadRightStickInnerDeadZone; } int32 ULyraSettingsShared::GetLatestDataVersion() const { // 0 = before subclassing ULocalPlayerSaveGame // 1 = first proper version return 1; } ULyraSettingsShared* ULyraSettingsShared::CreateTemporarySettings(const ULyraLocalPlayer* LocalPlayer) { // This is not loaded from disk but should be set up to save ULyraSettingsShared* SharedSettings = Cast(CreateNewSaveGameForLocalPlayer(ULyraSettingsShared::StaticClass(), LocalPlayer, SHARED_SETTINGS_SLOT_NAME)); SharedSettings->ApplySettings(); return SharedSettings; } ULyraSettingsShared* ULyraSettingsShared::LoadOrCreateSettings(const ULyraLocalPlayer* LocalPlayer) { // This will stall the main thread while it loads ULyraSettingsShared* SharedSettings = Cast(LoadOrCreateSaveGameForLocalPlayer(ULyraSettingsShared::StaticClass(), LocalPlayer, SHARED_SETTINGS_SLOT_NAME)); SharedSettings->ApplySettings(); return SharedSettings; } bool ULyraSettingsShared::AsyncLoadOrCreateSettings(const ULyraLocalPlayer* LocalPlayer, FOnSettingsLoadedEvent Delegate) { FOnLocalPlayerSaveGameLoadedNative Lambda = FOnLocalPlayerSaveGameLoadedNative::CreateLambda([Delegate] (ULocalPlayerSaveGame* LoadedSave) { ULyraSettingsShared* LoadedSettings = CastChecked(LoadedSave); LoadedSettings->ApplySettings(); Delegate.ExecuteIfBound(LoadedSettings); }); return ULocalPlayerSaveGame::AsyncLoadOrCreateSaveGameForLocalPlayer(ULyraSettingsShared::StaticClass(), LocalPlayer, SHARED_SETTINGS_SLOT_NAME, Lambda); } void ULyraSettingsShared::SaveSettings() { // Schedule an async save because it's okay if it fails AsyncSaveGameToSlotForLocalPlayer(); // TODO_BH: Move this to the serialize function instead with a bumped version number if (UEnhancedInputLocalPlayerSubsystem* System = ULocalPlayer::GetSubsystem(OwningPlayer)) { if (UEnhancedInputUserSettings* InputSettings = System->GetUserSettings()) { InputSettings->AsyncSaveSettings(); } } } void ULyraSettingsShared::ApplySettings() { ApplySubtitleOptions(); ApplyBackgroundAudioSettings(); ApplyCultureSettings(); if (UEnhancedInputLocalPlayerSubsystem* System = ULocalPlayer::GetSubsystem(OwningPlayer)) { if (UEnhancedInputUserSettings* InputSettings = System->GetUserSettings()) { InputSettings->ApplySettings(); } } } void ULyraSettingsShared::SetColorBlindStrength(int32 InColorBlindStrength) { InColorBlindStrength = FMath::Clamp(InColorBlindStrength, 0, 10); if (ColorBlindStrength != InColorBlindStrength) { ColorBlindStrength = InColorBlindStrength; FSlateApplication::Get().GetRenderer()->SetColorVisionDeficiencyType( (EColorVisionDeficiency)(int32)ColorBlindMode, (int32)ColorBlindStrength, true, false); } } int32 ULyraSettingsShared::GetColorBlindStrength() const { return ColorBlindStrength; } void ULyraSettingsShared::SetColorBlindMode(EColorBlindMode InMode) { if (ColorBlindMode != InMode) { ColorBlindMode = InMode; FSlateApplication::Get().GetRenderer()->SetColorVisionDeficiencyType( (EColorVisionDeficiency)(int32)ColorBlindMode, (int32)ColorBlindStrength, true, false); } } EColorBlindMode ULyraSettingsShared::GetColorBlindMode() const { return ColorBlindMode; } void ULyraSettingsShared::ApplySubtitleOptions() { if (USubtitleDisplaySubsystem* SubtitleSystem = USubtitleDisplaySubsystem::Get(OwningPlayer)) { FSubtitleFormat SubtitleFormat; SubtitleFormat.SubtitleTextSize = SubtitleTextSize; SubtitleFormat.SubtitleTextColor = SubtitleTextColor; SubtitleFormat.SubtitleTextBorder = SubtitleTextBorder; SubtitleFormat.SubtitleBackgroundOpacity = SubtitleBackgroundOpacity; SubtitleSystem->SetSubtitleDisplayOptions(SubtitleFormat); } } ////////////////////////////////////////////////////////////////////// void ULyraSettingsShared::SetAllowAudioInBackgroundSetting(ELyraAllowBackgroundAudioSetting NewValue) { if (ChangeValueAndDirty(AllowAudioInBackground, NewValue)) { ApplyBackgroundAudioSettings(); } } void ULyraSettingsShared::ApplyBackgroundAudioSettings() { if (OwningPlayer && OwningPlayer->IsPrimaryPlayer()) { FApp::SetUnfocusedVolumeMultiplier((AllowAudioInBackground != ELyraAllowBackgroundAudioSetting::Off) ? 1.0f : 0.0f); } } ////////////////////////////////////////////////////////////////////// void ULyraSettingsShared::ApplyCultureSettings() { if (bResetToDefaultCulture) { const FCulturePtr SystemDefaultCulture = FInternationalization::Get().GetDefaultCulture(); check(SystemDefaultCulture.IsValid()); const FString CultureToApply = SystemDefaultCulture->GetName(); if (FInternationalization::Get().SetCurrentCulture(CultureToApply)) { // Clear this string GConfig->RemoveKey(TEXT("Internationalization"), TEXT("Culture"), GGameUserSettingsIni); GConfig->Flush(false, GGameUserSettingsIni); } bResetToDefaultCulture = false; } else if (!PendingCulture.IsEmpty()) { // SetCurrentCulture may trigger PendingCulture to be cleared (if a culture change is broadcast) so we take a copy of it to work with const FString CultureToApply = PendingCulture; if (FInternationalization::Get().SetCurrentCulture(CultureToApply)) { // Note: This is intentionally saved to the users config // We need to localize text before the player logs in and very early in the loading screen GConfig->SetString(TEXT("Internationalization"), TEXT("Culture"), *CultureToApply, GGameUserSettingsIni); GConfig->Flush(false, GGameUserSettingsIni); } ClearPendingCulture(); } } void ULyraSettingsShared::ResetCultureToCurrentSettings() { ClearPendingCulture(); bResetToDefaultCulture = false; } const FString& ULyraSettingsShared::GetPendingCulture() const { return PendingCulture; } void ULyraSettingsShared::SetPendingCulture(const FString& NewCulture) { PendingCulture = NewCulture; bResetToDefaultCulture = false; bIsDirty = true; } void ULyraSettingsShared::OnCultureChanged() { ClearPendingCulture(); bResetToDefaultCulture = false; } void ULyraSettingsShared::ClearPendingCulture() { PendingCulture.Reset(); } bool ULyraSettingsShared::IsUsingDefaultCulture() const { FString Culture; GConfig->GetString(TEXT("Internationalization"), TEXT("Culture"), Culture, GGameUserSettingsIni); return Culture.IsEmpty(); } void ULyraSettingsShared::ResetToDefaultCulture() { ClearPendingCulture(); bResetToDefaultCulture = true; bIsDirty = true; } ////////////////////////////////////////////////////////////////////// void ULyraSettingsShared::ApplyInputSensitivity() { }