rokojori_action_library/Runtime/Conditions/HasCondition.cs

206 lines
4.0 KiB
C#
Raw Normal View History

using System.Collections.Generic;
using System.Reflection;
using Godot;
using Rokojori.Extensions;
namespace Rokojori
{
[Tool]
[GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Condition.svg")]
public partial class HasCondition: SceneCondition
{
bool _enteredTree = false;
public override void _EnterTree()
{
base._EnterTree();
_enteredTree = true;
}
void OnTargetAssignmentChanged()
{
// Deserialization sets exported properties before _EnterTree fires, so this
// only triggers for genuine interactive edits in the editor, never for a scene
// being loaded (regardless of property order) or for runtime reassignment.
if ( ! _enteredTree || ! Engine.IsEditorHint() )
{
return;
}
targetName = "";
#if TOOLS
NotifyPropertyListChanged();
#endif
}
GodotObject _target;
[Export]
public GodotObject target
{
get => _target;
set
{
if ( _target == value )
{
return;
}
_target = value;
OnTargetAssignmentChanged();
}
}
Node _targetNode;
[Export]
public Node targetNode
{
get => _targetNode;
set
{
if ( _targetNode == value )
{
return;
}
_targetNode = value;
OnTargetAssignmentChanged();
}
}
[Export]
public string targetName;
GodotObject GetEffectiveTarget()
{
return target != null ? target : (GodotObject) targetNode;
}
public override bool Evaluate()
{
var effectiveTarget = GetEffectiveTarget();
if ( effectiveTarget == null || string.IsNullOrEmpty( targetName ) )
{
return false;
}
var value = ReflectionHelper.GetDataMemberValue<object>( effectiveTarget, targetName );
return EvaluateValue( value );
}
static bool EvaluateValue( object value )
{
switch ( value )
{
case null:
return false;
case GodotObject go:
return GodotObject.IsInstanceValid( go );
case bool b:
return b;
case string s:
return ! string.IsNullOrEmpty( s );
case int i:
return i > 0;
case long l:
return l > 0;
case float f:
return f > 0;
case double d:
return d > 0;
default:
return false;
}
}
#if TOOLS
static bool IsSupportedMemberType( System.Type type )
{
if ( type == null )
{
return false;
}
if ( type == typeof( bool ) || type == typeof( string ) )
{
return true;
}
if ( type == typeof( int ) || type == typeof( long ) || type == typeof( float ) || type == typeof( double ) )
{
return true;
}
if ( typeof( Node ).IsAssignableFrom( type ) || typeof( Resource ).IsAssignableFrom( type ) )
{
return true;
}
return false;
}
List<string> GetTargetMemberNames()
{
var names = new List<string>();
var effectiveTarget = GetEffectiveTarget();
if ( effectiveTarget == null )
{
return names;
}
var infos = ReflectionHelper.GetDataMemberInfos( effectiveTarget, BindingFlags.Public | BindingFlags.Instance );
foreach ( var info in infos )
{
if ( ! info.IsDefined( typeof( ExportAttribute ) ) )
{
continue;
}
if ( ! IsSupportedMemberType( info.GetMemberType() ) )
{
continue;
}
names.Add( info.Name );
}
return names;
}
public override void _ValidateProperty( Godot.Collections.Dictionary property )
{
if ( property[ "name" ].AsString() != nameof( targetName ) )
{
return;
}
var members = GetTargetMemberNames();
property[ "hint" ] = (long) PropertyHint.Enum;
property[ "hint_string" ] = members.Count == 0
? "(Invalid Target)"
: "None," + string.Join( ",", members );
}
#endif
}
}