GameAudio.cs
namespace NiuLai;
/// <summary>
/// Movie-sampled audio. Paths are Assets/sounds/… compiled by sbox-dev.
/// </summary>
public static class GameAudio
{
public const string Song = "sounds/music/yu_hou_qing_feng";
public const string OpenSting = "sounds/sfx/open_sting";
public const string LarkNg = "sounds/sfx/lark_ng";
public const string LarkRide = "sounds/sfx/lark_ride";
public const string LikeHere = "sounds/sfx/like_here";
public const string MamaWake = "sounds/sfx/mama_wake";
public const string MamaOk = "sounds/sfx/mama_ok";
public const string SlowDown = "sounds/sfx/slow_down";
public const string Wolves = "sounds/sfx/wolves";
public const string WolfStare = "sounds/sfx/wolf_stare";
public const string Leopard = "sounds/sfx/leopard";
public const string CanRest = "sounds/sfx/can_rest";
public const string Moo = "sounds/sfx/moo";
public const string Cart = "sounds/sfx/cart";
public const string CartGrow = "sounds/sfx/cart_grow";
public const string Rain = "sounds/sfx/rain";
public const string Snake = "sounds/sfx/snake";
public const string Stand = "sounds/sfx/stand";
public const string Milk = "sounds/sfx/milk";
public const string NameNiu = "sounds/sfx/name_niu";
public const string CrowdWake = "sounds/sfx/crowd_wake";
public const string CrowdRide = "sounds/sfx/crowd_ride";
public const string CrowdMilk = "sounds/sfx/crowd_milk";
public const string CrowdBig = "sounds/sfx/crowd_big";
public const string CrowdCart = "sounds/sfx/crowd_cart";
public const string CrowdNg = "sounds/sfx/crowd_ng";
static SoundHandle _music;
static SoundHandle _oneshot;
static string _musicPath;
public static bool SongPlaying => _music.IsValid() && _musicPath == Song;
public static void PlaySong()
{
StartMusic( Song, 0.72f );
}
public static void PlayTitleSting()
{
PlayOne( OpenSting, 0.9f );
}
public static void StopMusic()
{
if ( _music.IsValid() )
_music.Stop();
_music = default;
_musicPath = null;
}
public static void PlayClip( string path, float volume = 0.95f )
{
PlayOne( path, volume );
}
public static void PlayCrowd( string path )
{
// Second layer: the room laughing, still muddy on purpose.
PlayLocal( path, 0.7f );
}
public static void PlayForHotspot( string id )
{
switch ( id )
{
case "lark":
PlayOne( GameState.HeardLikeHere ? LarkRide : LikeHere, 0.95f );
break;
case "mom":
if ( GameState.MomGone )
PlayOne( CanRest, 0.95f );
else if ( GameState.Beat == Beat.Night && !GameState.HasMilk )
PlayOne( MamaOk, 0.95f );
else
PlayOne( MamaWake, 0.95f );
break;
case "leopard":
PlayOne( GameState.GaveMilk ? Leopard : Milk, 0.95f );
break;
case "grass":
PlayOne( SlowDown, 0.9f );
break;
case "cart":
PlayOne( CartGrow, 0.95f );
break;
case "snake":
PlayOne( Snake, 0.95f );
break;
case "rock":
PlayOne( WolfStare, 0.95f );
break;
case "dad":
PlaySong();
break;
}
}
public static void PlayWolvesAt( Vector3 pos )
{
try
{
var h = Sound.Play( Wolves, pos );
if ( h.IsValid() )
h.Volume = 0.9f;
}
catch
{
PlayOne( Wolves, 0.85f );
}
}
static void StartMusic( string path, float volume )
{
if ( _music.IsValid() && _musicPath == path )
return;
StopMusic();
_music = PlayLocal( path, volume );
_musicPath = _music.IsValid() ? path : null;
if ( !_music.IsValid() )
Log.Warning( $"[Niu Lai] music missing: {path} — play once in sbox-dev to compile .vsnd" );
}
static void PlayOne( string path, float volume )
{
if ( _oneshot.IsValid() )
_oneshot.Stop();
_oneshot = PlayLocal( path, volume );
}
static SoundHandle PlayLocal( string path, float volume )
{
if ( string.IsNullOrWhiteSpace( path ) )
return default;
var clean = path.Trim().Replace( '\\', '/' );
if ( clean.EndsWith( ".sound", StringComparison.OrdinalIgnoreCase ) )
clean = clean[..^6];
var candidates = new[]
{
clean + ".sound",
clean,
"sounds/" + clean.TrimStart( '/' ).Replace( "sounds/", "" ) + ".sound",
};
foreach ( var candidate in candidates )
{
try
{
if ( ResourceLibrary.Get<SoundEvent>( candidate ) is SoundEvent evt )
{
var fromEvt = Sound.Play( evt, Vector3.Zero );
if ( fromEvt.IsValid() )
{
fromEvt.ListenLocal = true;
fromEvt.SpacialBlend = 0f;
fromEvt.Volume = volume;
fromEvt.Pitch = 0.90f + Random.Shared.Float() * 0.16f;
return fromEvt;
}
}
}
catch { }
try
{
var h = Sound.Play( candidate, Vector3.Zero );
if ( !h.IsValid() )
continue;
h.ListenLocal = true;
h.SpacialBlend = 0f;
h.Volume = volume;
h.Pitch = 0.90f + Random.Shared.Float() * 0.16f;
return h;
}
catch { }
}
return default;
}
}