#nullable enable
using System.Collections.Immutable;
using System.Linq.Expressions;
using System.Reflection;
using Facepunch.ActionGraphs;
using Facepunch.ActionGraphs.Compilation;
namespace Sandbox.ActionGraphs;
using DisplayInfo = Facepunch.ActionGraphs.DisplayInfo;
[NodeDefinition]
internal class RunSceneEventNodeDefinition : NodeDefinition
{
public const string Ident = "scene.run";
public DisplayInfo DefaultDisplay { get; } =
new DisplayInfo( "Run Event", "Runs an event on all components implementing an interface.", "Scene", Icon: "bolt", Hidden: true );
public override DisplayInfo DisplayInfo => DefaultDisplay;
public PropertyDefinition InterfaceProperty { get; } = new( ParameterNames.Type, typeof( Type ), PropertyFlags.Required,
new DisplayInfo( "Interface Type", "Scene event interface type.", Hidden: true ) );
public PropertyDefinition MethodNameProperty { get; } = new( ParameterNames.Name, typeof( string ), PropertyFlags.Required,
new DisplayInfo( "Method Name", "Scene event method name to invoke.", Hidden: true ) );
public InputDefinition InputSignal { get; } = InputDefinition.PrimarySignal();
///
/// Optional target to post this event to.
/// Only components within this target and its descendants will receive the event.
/// If not provided, the whole will be used.
///
public InputDefinition TargetInput { get; }
public OutputDefinition OutputSignal { get; } = OutputDefinition.PrimarySignal();
public NodeBinding DefaultBinding { get; }
public RunSceneEventNodeDefinition( NodeLibrary nodeLibrary )
: base( nodeLibrary, Ident )
{
TargetInput = new InputDefinition( "target", typeof( GameObject ), 0,
DisplayInfo.FromAttributes( typeof( RunSceneEventNodeDefinition ).GetProperty( nameof( TargetInput ) )! ) with
{
Title = "Target"
} );
DefaultBinding = NodeBinding.Create( DefaultDisplay,
new[] { InterfaceProperty, MethodNameProperty },
new[] { InputSignal, TargetInput },
new[] { OutputSignal } );
}
private static IReadOnlyList GetEventMethods( Type type )
{
if ( !type.IsInterface || !type.GetInterfaces().Any( x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof( ISceneEvent<> ) ) )
{
return Array.Empty();
}
return type.GetMethods( BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly );
}
protected override NodeBinding OnBind( BindingSurface surface )
{
var binding = DefaultBinding;
surface = surface with
{
Properties = surface.Properties.With( ParameterNames.IsStatic, false ),
InputTypes = surface.InputTypes.Without( TargetInput.Name )
};
if ( !surface.Properties.TryGetValue( InterfaceProperty.Name, out var typeObj ) || typeObj is not Type type )
{
return binding;
}
if ( GetEventMethods( type ) is not { Count: > 0 } )
{
return binding with
{
Messages = new[]
{
new NodeBinding.ValidationMessage( InterfaceProperty, MessageLevel.Error,
$"Expected an interface implementing {nameof(ISceneEvent