Player/Car/Visuals/CarHorn.cs

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.

Networking
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();
	}
}