// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "SubtitleDisplaySubsystem.h" #include "GameFramework/SaveGame.h" #include "LyraSettingsShared.generated.h" UENUM(BlueprintType) enum class EColorBlindMode : uint8 { Off, // Deuteranope (green weak/blind) Deuteranope, // Protanope (red weak/blind) Protanope, // Tritanope(blue weak / bind) Tritanope }; UENUM(BlueprintType) enum class ELyraAllowBackgroundAudioSetting : uint8 { Off, AllSounds, Num UMETA(Hidden), }; UENUM(BlueprintType) enum class ELyraGamepadSensitivity : uint8 { Invalid = 0 UMETA(Hidden), Slow UMETA(DisplayName = "01 - Slow"), SlowPlus UMETA(DisplayName = "02 - Slow+"), SlowPlusPlus UMETA(DisplayName = "03 - Slow++"), Normal UMETA(DisplayName = "04 - Normal"), NormalPlus UMETA(DisplayName = "05 - Normal+"), NormalPlusPlus UMETA(DisplayName = "06 - Normal++"), Fast UMETA(DisplayName = "07 - Fast"), FastPlus UMETA(DisplayName = "08 - Fast+"), FastPlusPlus UMETA(DisplayName = "09 - Fast++"), Insane UMETA(DisplayName = "10 - Insane"), MAX UMETA(Hidden), }; class ULyraLocalPlayer; /** * ULyraSettingsShared - The "Shared" settings are stored as part of the USaveGame system, these settings are not machine * specific like the local settings, and are safe to store in the cloud - and 'share' them. Using the save game system * we can also store settings per player, so things like controller keybind preferences should go here, because if those * are stored in the local settings all users would get them. * * TODO NDarnell Future version rename this maybe to CloudSave? Even though these arent necessarily in the cloud... * maybe change Localsettings to LyraPlatformSettings, or DeviceSettings? Make this one UserSettings? TBD * * NOTE: I want to do Within=LocalPlayer, but SaveGames create the object in the transient package, instead * of getting to select the outer, maybe LoadGameFromMemory should have a variant, like LoadGameFromMemory_WithOuter, or maybe pass in * an optional outer. */ UCLASS(/*Within=LocalPlayer*/) class ULyraSettingsShared : public USaveGame { GENERATED_BODY() public: DECLARE_EVENT_OneParam(ULyraSettingsShared, FOnSettingChangedEvent, ULyraSettingsShared* Settings); FOnSettingChangedEvent OnSettingChanged; public: ULyraSettingsShared(); void Initialize(ULyraLocalPlayer* LocalPlayer); bool IsDirty() const { return bIsDirty; } void ClearDirtyFlag() { bIsDirty = false; } void SaveSettings(); static ULyraSettingsShared* LoadOrCreateSettings(const ULyraLocalPlayer* LocalPlayer); void ApplySettings(); public: //////////////////////////////////////////////////////// // Color Blind Options UFUNCTION() EColorBlindMode GetColorBlindMode() const; UFUNCTION() void SetColorBlindMode(EColorBlindMode InMode); UFUNCTION() int32 GetColorBlindStrength() const; UFUNCTION() void SetColorBlindStrength(int32 InColorBlindStrength); private: UPROPERTY() EColorBlindMode ColorBlindMode = EColorBlindMode::Off; UPROPERTY() int32 ColorBlindStrength = 10; //////////////////////////////////////////////////////// // Gamepad Vibration public: UFUNCTION() bool GetForceFeedbackEnabled() const { return bForceFeedbackEnabled; } UFUNCTION() void SetForceFeedbackEnabled(const bool NewValue) { ChangeValueAndDirty(bForceFeedbackEnabled, NewValue); } private: /** Is force feedback enabled when a controller is being used? */ UPROPERTY() bool bForceFeedbackEnabled = true; //////////////////////////////////////////////////////// // Gamepad Deadzone public: /** Getter for gamepad move stick dead zone value. */ UFUNCTION() float GetGamepadMoveStickDeadZone() const { return GamepadMoveStickDeadZone; } /** Setter for gamepad move stick dead zone value. */ UFUNCTION() void SetGamepadMoveStickDeadZone(const float NewValue) { ChangeValueAndDirty(GamepadMoveStickDeadZone, NewValue); } /** Getter for gamepad look stick dead zone value. */ UFUNCTION() float GetGamepadLookStickDeadZone() const { return GamepadLookStickDeadZone; } /** Setter for gamepad look stick dead zone value. */ UFUNCTION() void SetGamepadLookStickDeadZone(const float NewValue) { ChangeValueAndDirty(GamepadLookStickDeadZone, NewValue); } private: /** Holds the gamepad move stick dead zone value. */ UPROPERTY() float GamepadMoveStickDeadZone; /** Holds the gamepad look stick dead zone value. */ UPROPERTY() float GamepadLookStickDeadZone; //////////////////////////////////////////////////////// // Gamepad Trigger Haptics public: UFUNCTION() bool GetTriggerHapticsEnabled() const { return bTriggerHapticsEnabled; } UFUNCTION() void SetTriggerHapticsEnabled(const bool NewValue) { ChangeValueAndDirty(bTriggerHapticsEnabled, NewValue); } UFUNCTION() bool GetTriggerPullUsesHapticThreshold() const { return bTriggerPullUsesHapticThreshold; } UFUNCTION() void SetTriggerPullUsesHapticThreshold(const bool NewValue) { ChangeValueAndDirty(bTriggerPullUsesHapticThreshold, NewValue); } UFUNCTION() uint8 GetTriggerHapticStrength() const { return TriggerHapticStrength; } UFUNCTION() void SetTriggerHapticStrength(const uint8 NewValue) { ChangeValueAndDirty(TriggerHapticStrength, NewValue); } UFUNCTION() uint8 GetTriggerHapticStartPosition() const { return TriggerHapticStartPosition; } UFUNCTION() void SetTriggerHapticStartPosition(const uint8 NewValue) { ChangeValueAndDirty(TriggerHapticStartPosition, NewValue); } private: /** Are trigger haptics enabled? */ UPROPERTY() bool bTriggerHapticsEnabled = false; /** Does the game use the haptic feedback as its threshold for judging button presses? */ UPROPERTY() bool bTriggerPullUsesHapticThreshold = true; /** The strength of the trigger haptic effects. */ UPROPERTY() uint8 TriggerHapticStrength = 8; /** The start position of the trigger haptic effects */ UPROPERTY() uint8 TriggerHapticStartPosition = 0; //////////////////////////////////////////////////////// // Subtitles public: UFUNCTION() bool GetSubtitlesEnabled() const { return bEnableSubtitles; } UFUNCTION() void SetSubtitlesEnabled(bool Value) { ChangeValueAndDirty(bEnableSubtitles, Value); } UFUNCTION() ESubtitleDisplayTextSize GetSubtitlesTextSize() const { return SubtitleTextSize; } UFUNCTION() void SetSubtitlesTextSize(ESubtitleDisplayTextSize Value) { ChangeValueAndDirty(SubtitleTextSize, Value); ApplySubtitleOptions(); } UFUNCTION() ESubtitleDisplayTextColor GetSubtitlesTextColor() const { return SubtitleTextColor; } UFUNCTION() void SetSubtitlesTextColor(ESubtitleDisplayTextColor Value) { ChangeValueAndDirty(SubtitleTextColor, Value); ApplySubtitleOptions(); } UFUNCTION() ESubtitleDisplayTextBorder GetSubtitlesTextBorder() const { return SubtitleTextBorder; } UFUNCTION() void SetSubtitlesTextBorder(ESubtitleDisplayTextBorder Value) { ChangeValueAndDirty(SubtitleTextBorder, Value); ApplySubtitleOptions(); } UFUNCTION() ESubtitleDisplayBackgroundOpacity GetSubtitlesBackgroundOpacity() const { return SubtitleBackgroundOpacity; } UFUNCTION() void SetSubtitlesBackgroundOpacity(ESubtitleDisplayBackgroundOpacity Value) { ChangeValueAndDirty(SubtitleBackgroundOpacity, Value); ApplySubtitleOptions(); } void ApplySubtitleOptions(); private: UPROPERTY() bool bEnableSubtitles = true; UPROPERTY() ESubtitleDisplayTextSize SubtitleTextSize = ESubtitleDisplayTextSize::Medium; UPROPERTY() ESubtitleDisplayTextColor SubtitleTextColor = ESubtitleDisplayTextColor::White; UPROPERTY() ESubtitleDisplayTextBorder SubtitleTextBorder = ESubtitleDisplayTextBorder::None; UPROPERTY() ESubtitleDisplayBackgroundOpacity SubtitleBackgroundOpacity = ESubtitleDisplayBackgroundOpacity::Medium; //////////////////////////////////////////////////////// // Shared audio settings public: UFUNCTION() ELyraAllowBackgroundAudioSetting GetAllowAudioInBackgroundSetting() const { return AllowAudioInBackground; } UFUNCTION() void SetAllowAudioInBackgroundSetting(ELyraAllowBackgroundAudioSetting NewValue); void ApplyBackgroundAudioSettings(); private: UPROPERTY() ELyraAllowBackgroundAudioSetting AllowAudioInBackground = ELyraAllowBackgroundAudioSetting::Off; //////////////////////////////////////////////////////// // Culture / language public: /** Gets the pending culture */ const FString& GetPendingCulture() const; /** Sets the pending culture to apply */ void SetPendingCulture(const FString& NewCulture); // Called when the culture changes. void OnCultureChanged(); /** Clears the pending culture to apply */ void ClearPendingCulture(); bool IsUsingDefaultCulture() const; void ResetToDefaultCulture(); bool ShouldResetToDefaultCulture() const { return bResetToDefaultCulture; } void ApplyCultureSettings(); void ResetCultureToCurrentSettings(); private: /** The pending culture to apply */ UPROPERTY(Transient) FString PendingCulture; /* If true, resets the culture to default. */ bool bResetToDefaultCulture = false; //////////////////////////////////////////////////////// // Gamepad Sensitivity public: UFUNCTION() double GetMouseSensitivityX() const { return MouseSensitivityX; } UFUNCTION() void SetMouseSensitivityX(double NewValue) { ChangeValueAndDirty(MouseSensitivityX, NewValue); ApplyInputSensitivity(); } UFUNCTION() double GetMouseSensitivityY() const { return MouseSensitivityY; } UFUNCTION() void SetMouseSensitivityY(double NewValue) { ChangeValueAndDirty(MouseSensitivityY, NewValue); ApplyInputSensitivity(); } UFUNCTION() double GetTargetingMultiplier() const { return TargetingMultiplier; } UFUNCTION() void SetTargetingMultiplier(double NewValue) { ChangeValueAndDirty(TargetingMultiplier, NewValue); ApplyInputSensitivity(); } UFUNCTION() bool GetInvertVerticalAxis() const { return bInvertVerticalAxis; } UFUNCTION() void SetInvertVerticalAxis(bool NewValue) { ChangeValueAndDirty(bInvertVerticalAxis, NewValue); ApplyInputSensitivity(); } UFUNCTION() bool GetInvertHorizontalAxis() const { return bInvertHorizontalAxis; } UFUNCTION() void SetInvertHorizontalAxis(bool NewValue) { ChangeValueAndDirty(bInvertHorizontalAxis, NewValue); ApplyInputSensitivity(); } private: /** Holds the mouse horizontal sensitivity */ UPROPERTY() double MouseSensitivityX = 1.0; /** Holds the mouse vertical sensitivity */ UPROPERTY() double MouseSensitivityY = 1.0; /** Multiplier applied while Aiming down sights. */ UPROPERTY() double TargetingMultiplier = 0.5; /** If true then the vertical look axis should be inverted */ UPROPERTY() bool bInvertVerticalAxis = false; /** If true then the horizontal look axis should be inverted */ UPROPERTY() bool bInvertHorizontalAxis = false; //////////////////////////////////////////////////////// // Gamepad Sensitivity public: UFUNCTION() ELyraGamepadSensitivity GetGamepadLookSensitivityPreset() const { return GamepadLookSensitivityPreset; } UFUNCTION() void SetLookSensitivityPreset(ELyraGamepadSensitivity NewValue) { ChangeValueAndDirty(GamepadLookSensitivityPreset, NewValue); ApplyInputSensitivity(); } UFUNCTION() ELyraGamepadSensitivity GetGamepadTargetingSensitivityPreset() const { return GamepadTargetingSensitivityPreset; } UFUNCTION() void SetGamepadTargetingSensitivityPreset(ELyraGamepadSensitivity NewValue) { ChangeValueAndDirty(GamepadTargetingSensitivityPreset, NewValue); ApplyInputSensitivity(); } void ApplyInputSensitivity(); private: UPROPERTY() ELyraGamepadSensitivity GamepadLookSensitivityPreset = ELyraGamepadSensitivity::Normal; UPROPERTY() ELyraGamepadSensitivity GamepadTargetingSensitivityPreset = ELyraGamepadSensitivity::Normal; //////////////////////////////////////////////////////// /// Dirty and Change Reporting private: template bool ChangeValueAndDirty(T& CurrentValue, const T& NewValue) { if (CurrentValue != NewValue) { CurrentValue = NewValue; bIsDirty = true; OnSettingChanged.Broadcast(this); return true; } return false; } bool bIsDirty = false; UPROPERTY(Transient) ULyraLocalPlayer* OwningPlayer = nullptr; };