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