DiamondSettings.cs
using System;
using Sandbox;
namespace Diamonds;
/// <summary>
/// Player preferences from the options overlay, persisted as JSON in the game's local data folder.
/// Volumes are slider positions from zero to one, applied as linear gain.
/// </summary>
public sealed class DiamondSettings
{
public const string FilePath = "diamonds_settings.json";
static DiamondSettings current;
/// <summary>Use a dim static dye background instead of gameplay fluid. Keep the original JSON key for saved preferences.</summary>
[System.Text.Json.Serialization.JsonPropertyName( "HideDye" )]
public bool DisableFluid { get; set; }
/// <summary>Overall level for every game sound, including music.</summary>
public float MasterVolume { get => masterVolume; set => masterVolume = Clamp( value ); }
float masterVolume = 1;
/// <summary>Music level, applied on top of the master volume.</summary>
public float MusicVolume { get => musicVolume; set => musicVolume = Clamp( value ); }
float musicVolume = 1;
/// <summary>Controller vibration strength, from zero (off) to one.</summary>
public float ControllerRumble { get => controllerRumble; set => controllerRumble = Clamp( value ); }
float controllerRumble = 1;
public const float RestartDelayStep = 0.05f;
/// <summary>Seconds the restart key must be held, from zero to one in 0.05 steps. Zero restarts instantly, without a fade.</summary>
public float RestartDelay
{
get => restartDelay;
set => restartDelay = float.IsFinite( value ) ? MathF.Round( Math.Clamp( value, 0, 1 ) / RestartDelayStep ) * RestartDelayStep : 0.5f;
}
float restartDelay = 0.5f;
/// <summary>A delay as the options slider shows it: 0s, 0.05s, 0.5s, 1s.</summary>
public static string FormatSeconds( float seconds )
{
// Whole hundredths, formatted by hand so the decimal point never follows the system culture.
int hundredths = Math.Max( 0, (int)MathF.Round( seconds * 100 ) );
int whole = hundredths / 100, fraction = hundredths % 100;
return fraction == 0 ? $"{whole}s" : fraction % 10 == 0 ? $"{whole}.{fraction / 10}s" : $"{whole}.{fraction:00}s";
}
public static DiamondSettings Current => current ??= Load();
/// <summary>Linear gain for master-scaled sound effects.</summary>
public static float EffectsGain => Current.MasterVolume;
/// <summary>Linear gain for music: the master and music sliders combined.</summary>
public static float MusicGain => Current.MasterVolume * Current.MusicVolume;
static DiamondSettings Load()
{
try { return FileSystem.Data?.ReadJsonOrDefault( FilePath, new DiamondSettings() ) ?? new DiamondSettings(); }
catch ( Exception e )
{
Log.Warning( $"Could not read {FilePath}; using default options. {e.Message}" );
return new DiamondSettings();
}
}
public static void Save()
{
try { FileSystem.Data?.WriteJson( FilePath, Current ); }
catch ( Exception e ) { Log.Warning( $"Could not save {FilePath}. {e.Message}" ); }
}
static float Clamp( float value ) => float.IsFinite( value ) ? Math.Clamp( value, 0, 1 ) : 1;
}