Weapons/ToolGun/Modes/Button/ButtonEntity.cs
/// <summary>
/// Determines whether the button is active only briefly when pressed, or alternates
/// between on and off for each press.
/// </summary>
public enum ButtonMode
{
	Hold,
	Toggle
}

[Alias( "button" ), Title( "Button" )]
public sealed class ButtonEntity : Component, Component.IPressable
{
	[Property, Sync, Hide, SignalOutput( Default = true )]
	public SignalOutput Pressed { get; set; } = new();

	[Property, Sync, ClientEditable, Group( "Behavior" )]
	public ButtonMode Mode { get; set; } = ButtonMode.Hold;

	[Property, Group( "Visual" )]
	public GameObject Cap { get; set; }

	[Property, Range( 0, 16 ), Group( "Visual" )]
	public float Travel { get; set; } = 2f;

	[Sync]
	public bool IsPressed { get; private set; }

	private Vector3 _capRestPosition;
	private Player _worldController;

	protected override void OnStart()
	{
		if ( Cap.IsValid() )
			_capRestPosition = Cap.LocalPosition;
	}

	protected override void OnUpdate()
	{
		if ( Cap.IsValid() )
			Cap.LocalPosition = _capRestPosition + Vector3.Down * (IsPressed ? Travel : 0f);
	}

	protected override void OnDisabled()
	{
		if ( Networking.IsHost )
			Pressed.Emit( this, false, _worldController );

		IsPressed = false;
		_worldController = null;
	}

	protected override void OnFixedUpdate()
	{
		if ( !Networking.IsHost ) return;

		Pressed.Emit( this, IsPressed, _worldController );
	}

	IPressable.Tooltip? IPressable.GetTooltip( IPressable.Event e )
	{
		var title = Mode == ButtonMode.Toggle ? "Toggle Button" : "Press Button";
		return new IPressable.Tooltip( title, "touch_app", null );
	}

	bool IPressable.CanPress( IPressable.Event e ) => Enabled;

	bool IPressable.Press( IPressable.Event e )
	{
		PressFromWorld( e.Source.GameObject );
		return true;
	}

	bool IPressable.Pressing( IPressable.Event e ) => Mode == ButtonMode.Hold;

	void IPressable.Release( IPressable.Event e )
	{
		ReleaseFromWorld( e.Source.GameObject );
	}

	[Rpc.Host]
	void PressFromWorld( GameObject presser )
	{
		if ( !presser.IsValid() ) return;

		_worldController = presser.Root.GetComponent<Player>();
		if ( Mode == ButtonMode.Toggle )
			IsPressed = !IsPressed;
		else
			IsPressed = true;
	}

	[Rpc.Host]
	void ReleaseFromWorld( GameObject presser )
	{
		if ( Mode != ButtonMode.Hold ) return;

		var controller = presser?.Root.GetComponent<Player>();
		if ( _worldController.IsValid() && controller != _worldController ) return;

		IsPressed = false;
	}
}