Game/2D/MusicManager.cs
using Sandbox;

public sealed class MusicManager : Component
{
    public static MusicManager Instance { get; private set; }

    SoundHandle _menuMusic;
    SoundHandle _windMusic;
    SoundHandle _gameMusic;

    // Tracked explicitly rather than inferred from SoundHandle.IsValid(), because
    // a handle stopped with a fade is not reliably invalid straight away — and
    // guessing wrong here means the menu comes back silent.
    bool _menuPlaying;

    protected override void OnStart()
    {
        Instance = this;

        // Subscribe to game events
        if (ChainReactionGame.Instance != null)
        {
            ChainReactionGame.Instance.OnGameStarted  += OnGameStarted;
            ChainReactionGame.Instance.OnGameReturned += OnGameReturned;
        }

        StartMenuLoops(.25f);
    }

    void StartMenuLoops(float volume)
    {
        _menuMusic   = Sound.Play("phonk_menu_music", volume);
        _windMusic   = Sound.Play("wind_blowing_loop", volume);
        _menuPlaying = true;
    }

    void StopMenuLoops(float fade)
    {
        if (_menuMusic.IsValid()) _menuMusic.Stop(fade);
        if (_windMusic.IsValid()) _windMusic.Stop(fade);
        _menuPlaying = false;
    }

    void OnGameStarted()
    {
        // Reactor mode is meant to be left running for a long time — the combat
        // track would grate after ten minutes, so it keeps the calmer menu loop.
        if (ChainReactionGame.Instance?.Mode == GameMode.Idle)
        {
            if (_gameMusic.IsValid()) _gameMusic.Stop(.5f);

            if (!_menuPlaying) StartMenuLoops(.18f);
            else if (_menuMusic.IsValid()) _menuMusic.Volume = .18f;

            return;
        }

        StopMenuLoops(.5f);

        // Only start game music if not already playing
        if (!_gameMusic.IsValid())
            _gameMusic = Sound.Play("abydos_game");
    }

    public void StopMenuMusic() => StopMenuLoops(0f);

    public void PlayMenuMusic() => StartMenuLoops(.25f);

    void OnGameReturned()
    {
        // Reactor mode never starts the combat track, so this handle can still be
        // default-constructed here — calling Stop() on that throws.
        if (_gameMusic.IsValid()) _gameMusic.Stop(.5f);

        // Coming back from Reactor mode the menu loop never stopped — restore
        // its volume instead of stacking a second copy on top of it.
        if (_menuPlaying)
        {
            if (_menuMusic.IsValid()) _menuMusic.Volume = .3f;
            return;
        }

        StartMenuLoops(.3f);
    }
}