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( 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 GetTargetMemberNames() { var names = new List(); 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 } }