Editor/ActionGraphLabelSource.cs
using Editor;
using Facepunch.ActionGraphs;
using System;
using System.Linq;
using System.Text;
using Sandbox.ActionGraphs;
namespace Sandbox.States.Editor;
public abstract record ActionGraphLabelSource<T> : ILabelSource
where T : Delegate
{
protected abstract StateMachineView View { get; }
protected StateMachineComponent StateMachine => View.StateMachine;
protected abstract string ContextName { get; }
public abstract string Title { get; }
public string? Icon => ActionGraph is { } graph ? graph.HasErrors() ? "error" : string.IsNullOrEmpty( graph.Icon ) ? DefaultIcon : graph.Icon : null;
public string? Text => ActionGraph is { } graph ? graph.Title ?? "Unnamed" : null;
public abstract string? Description { get; }
public bool IsValid => Delegate is not null;
string? ILabelSource.Description
{
get
{
var builder = new StringBuilder();
builder.Append( $"<p>{Description}</p>" );
if ( ActionGraph is { } graph )
{
if ( graph.Description is { } desc )
{
builder.Append( $"<p>{desc}</p>" );
}
if ( graph.HasErrors() )
{
builder.Append( "<p><font color=\"#ff0000\">" );
foreach ( var message in graph.Messages.Where( x => x.IsError ) )
{
builder.AppendLine( message.Value );
}
builder.Append( "</font></p>" );
}
}
return builder.ToString();
}
}
public Color? Color => ActionGraph is { } graph && graph.HasErrors() ? global::Color.Red.Darken( 0.05f ) : (Color?)null;
protected abstract string DefaultIcon { get; }
protected abstract T? Delegate { get; set; }
protected ActionGraph? ActionGraph
{
get => Delegate.GetActionGraphInstance() is { Graph: var graph } ? graph : null;
set => Delegate = value?.CreateDelegate<T>().Delegate;
}
public void BuildAddContextMenu( global::Editor.Menu menu )
{
if ( ActionGraph is not null ) return;
menu.AddOption( $"Add {Title}", DefaultIcon, action: CreateOrEdit );
}
public void BuildModifyContextMenu( global::Editor.Menu menu )
{
menu.AddHeading( Title );
menu.AddOption( "Edit", "edit", action: CreateOrEdit );
menu.AddOption( "Clear", "clear", action: () =>
{
View.LogEdit( $"{ContextName} {Title} Removed" );
Delegate = null;
ForceUpdate();
} );
}
private void CreateOrEdit()
{
if ( Delegate is null )
{
View.LogEdit( $"{ContextName} {Title} Added" );
Delegate = CreateGraph( Title );
EditorEvent.Run( "actiongraph.inspect", ActionGraph );
ForceUpdate();
}
else
{
EditorEvent.Run( "actiongraph.inspect", ActionGraph );
}
}
public void Delete()
{
Delegate = null;
}
public void DoubleClick()
{
CreateOrEdit();
}
protected abstract void ForceUpdate();
protected T CreateGraph( string title )
{
var deleg = ActionGraph.CreateDelegate<T>( EditorNodeLibrary );
var graph = deleg.Graph;
graph.Title = title;
graph.SourceLocation = StateMachine.GameObject.Scene.GetSourceLocation();
graph.SetParameters(
graph.Inputs.Values.Concat( InputDefinition.Target( typeof( GameObject ), StateMachine.GameObject ) ),
graph.Outputs.Values.ToArray() );
return deleg.Delegate;
}
}
public abstract record StateAction( StateItem Item ) : ActionGraphLabelSource<Action>
{
public State State => Item.State;
protected override StateMachineView View => Item.View;
protected override string ContextName => "State";
protected override void ForceUpdate()
{
Item.ForceUpdate();
}
}
public record StateEnterAction( StateItem Item ) : StateAction( Item )
{
public override string Title => "Enter";
public override string Description => "Action performed when entering this state.";
protected override string DefaultIcon => "login";
protected override Action? Delegate
{
get => State.OnEnterState;
set => State.OnEnterState = value;
}
}
public record StateUpdateAction( StateItem Item ) : StateAction( Item )
{
public override string Title => "Update";
public override string Description => "Action performed every fixed update while in this state.";
protected override string DefaultIcon => "update";
protected override Action? Delegate
{
get => State.OnUpdateState;
set => State.OnUpdateState = value;
}
}
public record StateLeaveAction( StateItem Item ) : StateAction( Item )
{
public override string Title => "Leave";
public override string Description => "Action performed when leaving this state.";
protected override string DefaultIcon => "logout";
protected override Action? Delegate
{
get => State.OnLeaveState;
set => State.OnLeaveState = value;
}
}
public abstract record TransitionActionGraph<T>( TransitionItem Item ) : ActionGraphLabelSource<T>
where T : Delegate
{
public Transition Transition => Item.Transition!;
protected override StateMachineView View => Item.Source.View;
protected override string ContextName => "Transition";
protected override void ForceUpdate()
{
Item.ForceUpdate();
}
}
public record TransitionCondition( TransitionItem Item ) : TransitionActionGraph<Func<bool>>( Item )
{
public override string Title => "Condition";
public override string Description => "This transition will only be taken if this expression is true.";
protected override string DefaultIcon => "question_mark";
protected override Func<bool>? Delegate
{
get => Transition.Condition;
set => Transition.Condition = value;
}
}
public record TransitionAction( TransitionItem Item ) : TransitionActionGraph<Action>( Item )
{
public override string Title => "Action";
public override string Description => "Action performed when this transition is taken.";
protected override string DefaultIcon => "directions_run";
protected override Action? Delegate
{
get => Transition.OnTransition;
set => Transition.OnTransition = value;
}
}