Component attached to a car that manages a looping horn sound. The local player sets IsBeeping from input and that synced boolean causes all clients to start, update position, or stop a looping SoundHandle.
namespace Machines.Player;
/// <summary>
/// Looping car horn: local player drives IsBeeping from input; all clients play/stop the loop from that synced state.
/// </summary>
public sealed class CarHorn : Component
{
[RequireComponent]
public Car Car { get; private set; }
/// <summary>
/// The looping horn sound event to play while beeping.
/// </summary>
[Property, Group( "Sound" )]
public SoundEvent HornSound { get; set; }
/// <summary>
/// True while the horn is held; set by the local player, synced to everyone.
/// </summary>
[Sync]
public bool IsBeeping { get; private set; }
private SoundHandle _sound;
protected override void OnUpdate()
{
// Local player drives the state from input
if ( Car.IsValid() && Car.IsLocalPlayer )
{
IsBeeping = Input.Down( "Horn" );
}
// Everyone plays/stops the loop from the synced state
if ( IsBeeping )
{
if ( !_sound.IsValid() || !_sound.IsPlaying )
{
if ( HornSound.IsValid() )
_sound = Sound.Play( HornSound, WorldPosition );
}
if ( _sound.IsValid() )
_sound.Position = WorldPosition;
}
else
{
StopSound();
}
}
private void StopSound()
{
// Fade out over 0.2s and drop the reference; the next beep plays a fresh handle.
_sound?.Stop( 0.2f );
_sound = null;
}
protected override void OnDisabled()
{
IsBeeping = false;
StopSound();
}
protected override void OnDestroy()
{
StopSound();
}
}