206 lines
4.0 KiB
C#
206 lines
4.0 KiB
C#
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|