Map/Button.cs
[Alias( "Button" ), EditorHandle( Icon = "touch_app" )]
public sealed class Button : Component, Component.IPressable
{
	public enum ButtonMode
	{

		Toggle,

		Continuous,

	
		Immediate
	}

	[Property]
	public ButtonMode Mode { get; set; } = ButtonMode.Toggle;

	[Property, ShowIf( "Mode", ButtonMode.Toggle )]
	public bool AutoReset { get; set; } = true;

	[Property, ShowIf( "AutoReset", true )]
	public float ResetTime { get; set; } = 1.0f;

	[Property, FeatureEnabled( "Movement", Icon = "edgesensor_high" )]
	public bool Move { get; set; }

	[Property, Feature( "Movement" ), ShowIf( nameof( Move ), true )]
	public GameObject MoveTarget { get; set; }

	[Property, Feature( "Movement" ), ShowIf( nameof( Move ), true )]
	public Vector3 MoveDelta { get; set; }

	[Property, Feature( "Movement" ), ShowIf( nameof( Move ), true )]
	public Curve AnimationCurve { get; set; } = new Curve(
		new Curve.Frame( 0f, 0f ),
		new Curve.Frame( 1f, 1.0f )
	);


	[Property, Group( "Movement" ), ShowIf( nameof( Move ), true )]
	public float AnimationTime { get; set; } = 0.5f;

	[Property, Feature( "Sound", Icon = "volume_up" )]
	public SoundEvent OnSound { get; set; }

	[Property, Feature( "Sound" )]
	public SoundEvent OffSound { get; set; }


	[Property, Feature( "Events", Icon = "double_arrow" )]
	public Action<bool> OnStateChanged { get; set; }

	Vector3 _initialPosition;
	Transform _startTransform;
	bool _isBeingPressed;
	bool _shouldTurnOffNextFrame;

	[Sync]
	private TimeSince LastUse { get; set; }

	[Sync]
	private bool _isOn { get; set; }

	[Doo.ArgumentHint<GameObject>( "user" )]
	[Property]
	public Doo OnPressed { get; set; }


	public bool IsOn
	{
		get => _isOn;
		private set
		{
			if ( _isOn == value )
				return;

			_isOn = value;
			OnButtonStateChanged( value );
		}
	}


	public bool IsAnimating { get; private set; }

	void OnButtonStateChanged( bool isOn )
	{
		OnStateChanged?.Invoke( isOn );
	}

	protected override void DrawGizmos()
	{
		if ( !Gizmo.IsSelected )
			return;

		if ( !Move )
			return;

		Gizmo.Transform = WorldTransform;

		var bbox = GameObject.GetLocalBounds();
		bbox += MoveDelta * MathF.Sin( RealTime.Now * 2.0f ).Remap( -1, 1 );

		Gizmo.Draw.Color = Color.Yellow;
		Gizmo.Draw.LineThickness = 3;
		Gizmo.Draw.LineBBox( bbox );
		Gizmo.Draw.IgnoreDepth = true;

		Gizmo.Draw.LineThickness = 1;
		Gizmo.Draw.Color = Gizmo.Draw.Color.WithAlpha( 0.3f );
		Gizmo.Draw.LineBBox( bbox );
	}

	protected override void OnStart()
	{
		var moveTarget = MoveTarget.IsValid()
			? MoveTarget
			: GameObject;

		_startTransform = moveTarget.LocalTransform;
		_initialPosition = _startTransform.Position;
	}

	bool IPressable.CanPress( IPressable.Event e )
	{
		return !IsAnimating;
	}

	bool IPressable.Pressing( IPressable.Event e )
	{
		_isBeingPressed = true;

		return Mode == ButtonMode.Continuous;
	}

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

	void IPressable.Release( IPressable.Event e )
	{
		_isBeingPressed = false;
		Release( e.Source.GameObject );
	}


	[Rpc.Host]
	[ActionGraphNode( "sandbox.button.on" ), Pure]
	public void TurnOn( GameObject presser = null )
	{
		if ( IsOn || IsAnimating )
			return;

		LastUse = 0;
		IsAnimating = Move;
		IsOn = true;

		if ( OnSound is not null )
			PlaySound( OnSound );

		if ( Mode == ButtonMode.Immediate )
		{
			_shouldTurnOffNextFrame = true;
		}
	}


	[Rpc.Host]
	[ActionGraphNode( "sandbox.button.off" ), Pure]
	public void TurnOff()
	{
		if ( !IsOn || IsAnimating )
			return;

		LastUse = 0;
		IsAnimating = Move;
		IsOn = false;

		if ( OffSound is not null )
			PlaySound( OffSound );
	}

	
	[Rpc.Host]
	[ActionGraphNode( "sandbox.button.toggle" ), Pure]
	public void Toggle( GameObject presser )
	{
		if ( !IsOn )
		{
			TurnOn( presser );
		}
		else
		{
			TurnOff();
		}
	}

	[Rpc.Host]
	private void Press( GameObject presser )
	{
		if ( IsAnimating )
			return;

		//fortnite4

		switch ( Mode )
		{
			case ButtonMode.Toggle:
				if ( IsOn && !AutoReset )
				{
					TurnOff();
				}
				else
				{
					TurnOn( presser );
				}
				break;

			case ButtonMode.Continuous:
				TurnOn( presser );
				break;

			case ButtonMode.Immediate:
				TurnOn( presser );
				break;
		}
	}

	[Rpc.Host]
	private void Release( GameObject presser )
	{
		if ( Mode == ButtonMode.Continuous && IsOn )
		{
			TurnOff();
		}
	}

	[Rpc.Broadcast]
	private void PlaySound( SoundEvent sound )
	{
		GameObject.PlaySound( sound );
	}

	protected override void OnFixedUpdate()
	{
		if ( Mode == ButtonMode.Immediate &&
			_shouldTurnOffNextFrame &&
			IsOn &&
			!IsAnimating )
		{
			_shouldTurnOffNextFrame = false;
			TurnOff();
			return;
		}

//wspwspwsp
		if ( Mode == ButtonMode.Toggle &&
			!IsAnimating &&
			IsOn &&
			AutoReset &&
			ResetTime >= 0.0f &&
			LastUse >= ResetTime )
		{
			TurnOff();
			return;
		}
		if ( Mode == ButtonMode.Continuous &&
			IsOn &&
			!IsAnimating &&
			!_isBeingPressed )
		{
			TurnOff();
		}

	
		if ( Mode == ButtonMode.Continuous )
		{
			_isBeingPressed = false;
		}
		if ( !IsAnimating )
			return;

		
		var time = LastUse.Relative.Remap(
			0.0f,
			AnimationTime,
			0.0f,
			1.0f
		);

		
		var curve = AnimationCurve.Evaluate( time );

	
		if ( !IsOn )
			curve = 1.0f - curve;

		if ( Move )
		{
			var target = MoveTarget.IsValid()
				? MoveTarget
				: GameObject;

			var targetPosition =
				_initialPosition +
				target.LocalTransform.Rotation * (MoveDelta * curve);

			target.LocalTransform =
				_startTransform.WithPosition( targetPosition );
		}

		
		if ( time < 1f )
			return;

		IsAnimating = false;
	}

	[Property, Feature( "Tooltip" )]
	public string TooltipTitle { get; set; } = "Press";

	[Property, Feature( "Tooltip" )]
	public string TooltipIcon { get; set; } = "touch_app";

	[Property, Feature( "Tooltip" )]
	public string TooltipDescription { get; set; } = "";

	[Header( "Off State" )]
	[ShowIf( "Mode", ButtonMode.Toggle )]
	[Property, Feature( "Tooltip" )]
	public string TooltipTitleOff { get; set; } = "Press";

	[ShowIf( "Mode", ButtonMode.Toggle )]
	[Property, Feature( "Tooltip" )]
	public string TooltipIconOff { get; set; } = "touch_app";

	[ShowIf( "Mode", ButtonMode.Toggle )]
	[Property, Feature( "Tooltip" )]
	public string TooltipDescriptionOff { get; set; } = "";

	IPressable.Tooltip? IPressable.GetTooltip( IPressable.Event e )
	{
		if ( string.IsNullOrWhiteSpace( TooltipTitle ) &&
			string.IsNullOrWhiteSpace( TooltipIcon ) )
		{
			return default;
		}

		if ( Mode == ButtonMode.Toggle && IsOn )
		{
			return new IPressable.Tooltip(
				TooltipTitleOff,
				TooltipIconOff,
				TooltipDescriptionOff
			);
		}

		return new IPressable.Tooltip(
			TooltipTitle,
			TooltipIcon,
			TooltipDescription
		);
	}
}