Game/AudioSettings.cs
namespace Monolith;
/// <summary>
/// Sound and music volume, persisted separately from progress.
///
/// **Why its own file rather than a field on <c>SaveData</c>.** These are device settings, not
/// progress. Someone who collapses and starts a fresh run should not get their audio turned back
/// on, and more practically, the music component reads this during <c>OnStart</c>, which can run
/// before <see cref="PlayerProgress"/> has loaded anything. A static with its own file has no
/// ordering problem to solve.
///
/// **Why volumes and not booleans.** The request was for a mute, and mute is what the menu
/// offers, but storing a float costs nothing extra and means a later "50%" option is a UI change
/// rather than a save format change. Muted is simply zero.
/// </summary>
public static class AudioSettings
{
private const string SettingsPath = "audio.json";
/// <summary>The stored shape. Public because the JSON serialiser needs to see the fields.</summary>
public sealed class Data
{
public float Music { get; set; } = 1f;
public float Effects { get; set; } = 1f;
}
private static Data current;
private static Data Current
{
get
{
if ( current != null )
return current;
try
{
current = FileSystem.Data.ReadJsonOrDefault<Data>( SettingsPath, null ) ?? new Data();
}
catch ( Exception e )
{
Log.Warning( $"Could not load audio settings, using defaults: {e.Message}" );
current = new Data();
}
return current;
}
}
/// <summary>Music volume, 0 to 1. Read every frame by <see cref="MonolithMusic"/>.</summary>
public static float MusicVolume => Current.Music;
/// <summary>Sound effect volume, 0 to 1. Applied inside <see cref="Audio"/>.</summary>
public static float EffectsVolume => Current.Effects;
public static bool MusicMuted => MusicVolume <= 0.001f;
public static bool EffectsMuted => EffectsVolume <= 0.001f;
public static void ToggleMusic()
{
Current.Music = MusicMuted ? 1f : 0f;
Save();
}
public static void ToggleEffects()
{
Current.Effects = EffectsMuted ? 1f : 0f;
Save();
}
private static void Save()
{
try
{
FileSystem.Data.WriteJson( SettingsPath, Current );
}
catch ( Exception e )
{
Log.Warning( $"Could not save audio settings: {e.Message}" );
}
}
}