Files
unity/Assets/Game/Scripts/Settings/SettingsService.cs
T

153 lines
5.6 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Ironspore
{
public enum SettingsApplyResult { Saved, ConfirmDisplay, Failed }
// Runtime settings orchestration; UI owns its draft, while the store owns durable snapshots.
public sealed class SettingsService : IDisposable
{
readonly SettingsStore store;
readonly GameSettingsData defaults;
readonly float initialVolume;
readonly int initialQuality;
readonly Func<float> clock;
GameSettingsData current, preview;
float displayDeadline;
public InputBindingService Bindings { get; }
public GameSettingsData Current => current.Copy();
public GameSettingsData Defaults => defaults.Copy();
public bool ConfirmingDisplay => preview != null;
public int ConfirmationSeconds => Mathf.Max(0, Mathf.CeilToInt(displayDeadline - clock()));
public string Error => store.Error;
public float LookSensitivity => current.mouseSensitivity;
public float AimSensitivity => current.aimSensitivity;
public SettingsService(SettingsStore store, InputActionAsset input, GameSettingsData defaults, Func<float> clock = null)
{
this.store = store;
this.defaults = defaults.Copy();
this.clock = clock ?? (() => Time.realtimeSinceStartup);
initialVolume = AudioListener.volume;
initialQuality = QualitySettings.GetQualityLevel();
Bindings = new InputBindingService(input);
current = store.Data.Normalize(QualitySettings.names.Length);
if (!Bindings.TryLoad(current.bindingOverrides))
Debug.LogWarning("Invalid input overrides were ignored; using default controls.");
current.bindingOverrides = Bindings.Save();
ApplyRuntime(current, true);
}
public static GameSettingsData RuntimeDefaults() => new GameSettingsData
{
qualityLevel = Mathf.Min(2, QualitySettings.names.Length - 1),
resolutionWidth = Application.isEditor ? 1280 : Mathf.Max(640, Screen.width),
resolutionHeight = Application.isEditor ? 720 : Mathf.Max(360, Screen.height),
fullscreen = !Application.isEditor && Screen.fullScreen
};
public List<Vector2Int> Resolutions()
{
var sizes = new List<Vector2Int>();
foreach (var resolution in Screen.resolutions)
AddSize(sizes, resolution.width, resolution.height);
AddSize(sizes, 1280, 720);
AddSize(sizes, 1600, 900);
AddSize(sizes, 1920, 1080);
AddSize(sizes, current.resolutionWidth, current.resolutionHeight);
sizes.Sort((a, b) => a.x == b.x ? a.y.CompareTo(b.y) : a.x.CompareTo(b.x));
return sizes;
}
static void AddSize(List<Vector2Int> sizes, int width, int height)
{
var size = new Vector2Int(width, height);
if (width >= 640 && height >= 360 && !sizes.Contains(size))
sizes.Add(size);
}
public SettingsApplyResult Apply(GameSettingsData draft)
{
CancelDisplay();
var candidate = draft.Normalize(QualitySettings.names.Length);
candidate.bindingOverrides = Bindings.Save();
if (!candidate.SameDisplay(current))
{
preview = candidate;
displayDeadline = clock() + 15;
ApplyRuntime(preview, true);
return SettingsApplyResult.ConfirmDisplay;
}
return Save(candidate) ? SettingsApplyResult.Saved : SettingsApplyResult.Failed;
}
public bool ConfirmDisplay()
{
if (preview == null)
return false;
var candidate = preview;
preview = null;
if (Save(candidate))
return true;
ApplyRuntime(current, true);
Bindings.TryLoad(current.bindingOverrides);
return false;
}
bool Save(GameSettingsData candidate)
{
if (!store.Save(candidate))
return false;
current = candidate.Copy();
ApplyRuntime(current, false);
return true;
}
public bool Tick()
{
if (!ConfirmingDisplay || clock() < displayDeadline)
return false;
CancelDisplay();
return true;
}
public void CancelDisplay()
{
if (preview == null)
return;
preview = null;
ApplyRuntime(current, true);
}
public void DiscardDraft()
{
Bindings.Cancel();
CancelDisplay();
Bindings.TryLoad(current.bindingOverrides);
}
void ApplyRuntime(GameSettingsData data, bool display)
{
AudioListener.volume = data.masterVolume;
if (QualitySettings.GetQualityLevel() != data.qualityLevel)
QualitySettings.SetQualityLevel(data.qualityLevel, true);
if (display && !Application.isEditor)
Screen.SetResolution(data.resolutionWidth, data.resolutionHeight,
data.fullscreen ? FullScreenMode.ExclusiveFullScreen : FullScreenMode.Windowed);
}
public void Dispose()
{
DiscardDraft();
Bindings.Dispose();
#if UNITY_EDITOR
AudioListener.volume = initialVolume;
QualitySettings.SetQualityLevel(initialQuality, true);
#endif
}
}
}