@@ -11,123 +11,112 @@ namespace RemoteTech.FlightComputer
1111 public static class UIPartActionMenuPatcher
1212 {
1313 // UI types that we are actually hooking
14- public static Type [ ] AllowedFieldTypes = { typeof ( UIPartActionToggle ) , typeof ( UIPartActionFloatRange ) , typeof ( UIPartActionCycle ) } ;
14+ public static Type [ ] UIPartActionFieldItemAllowedTypes = { typeof ( UIPartActionToggle ) , typeof ( UIPartActionFloatRange ) , typeof ( UIPartActionCycle ) } ;
1515
16- public static List < string > parsedPartActions = new List < string > ( ) ;
16+ public static List < string > ParsedPartActions = new List < string > ( ) ;
1717
18- public static void WrapEvent ( Vessel parent , Action < BaseEvent , bool > passthrough )
18+ public static void WrapPartActionEventItem ( Part part , Action < BaseEvent , bool > passthrough )
1919 {
20- UIPartActionController controller = UIPartActionController . Instance ;
21- if ( ! controller ) return ;
20+ var controller = UIPartActionController . Instance ;
21+ if ( ! controller )
22+ return ;
2223
23- // Get the open context menus
24- FieldInfo listFieldInfo = controller . GetType ( ) . GetFields ( BindingFlags . Instance | BindingFlags . NonPublic )
25- . First ( fi => fi . FieldType == typeof ( List < UIPartActionWindow > ) ) ;
24+ // get the part action window corresponding to the part
25+ var window = controller . GetItem ( part ) ;
26+ if ( window == null )
27+ return ;
2628
27- List < UIPartActionWindow > openWindows = ( List < UIPartActionWindow > ) listFieldInfo . GetValue ( controller ) ;
29+ // get all the items that makes this window (toggle buttons, sliders, etc.)
30+ var partActionItems = window . ListItems ;
2831
29- foreach ( UIPartActionWindow window in openWindows . Where ( window => window . part . vessel == parent ) )
32+ // loop through all of those UI components
33+ for ( var i = 0 ; i < partActionItems . Count ( ) ; i ++ )
3034 {
31- // Get the list of all UIPartActionItem's
32- FieldInfo itemsFieldInfo = typeof ( UIPartActionWindow ) . GetFields ( BindingFlags . Instance | BindingFlags . NonPublic )
33- . First ( fi => fi . FieldType == typeof ( List < UIPartActionItem > ) ) ;
34-
35- List < UIPartActionItem > partActionItems = ( List < UIPartActionItem > ) itemsFieldInfo . GetValue ( window ) ;
36-
37- // We only need the UIPartActionEventItem's
38- UIPartActionItem [ ] actionEventButtons = partActionItems . Where ( l => ( l as UIPartActionEventItem ) != null ) . ToArray ( ) ;
39- for ( int i = 0 ; i < actionEventButtons . Count ( ) ; i ++ )
40- {
41- // get event from button
42- UIPartActionEventItem button = ( actionEventButtons [ i ] as UIPartActionEventItem ) ;
43- BaseEvent originalEvent = button . Evt ;
44-
45- // Search for the BaseEventDelegate (BaseEvent.onEvent) field defined for the current BaseEvent type.
46- // Note that 'onEvent' is protected, so we have to go through reflection.
47- FieldInfo partEventFieldInfo = typeof ( BaseEvent ) . GetFields ( BindingFlags . Instance | BindingFlags . NonPublic )
48- . First ( fi => fi . FieldType == typeof ( BaseEventDelegate ) ) ;
49-
50- // Get the actual value of the 'onEvent' field
51- BaseEventDelegate partEvent = ( BaseEventDelegate ) partEventFieldInfo . GetValue ( originalEvent ) ;
52-
53- // Gets the method represented by the delegate and from this method returns an array of custom attributes applied to this member.
54- // Simply put, we want all [KSPEvent] attributes applied to the BaseEventDelegate.Method field.
55- object [ ] customAttributes = partEvent . Method . GetCustomAttributes ( typeof ( KSPEvent ) , true ) ;
56-
57- // Look for the custom attribute skip_control
58- bool skip_control = customAttributes . Any ( a => ( ( KSPEvent ) a ) . category . Contains ( "skip_control" ) ) ;
59- if ( ! skip_control )
60- {
61- /*
62- * Override the old BaseEvent with our BaseEvent to the button
63- */
35+ // check that the part action item is actually a UIPartActionFieldItem (it could be a UIPartActionEventItem)
36+ var uiPartActionEventItem = ( partActionItems [ i ] as UIPartActionEventItem ) ;
37+ if ( uiPartActionEventItem == null )
38+ continue ;
39+
40+ // get event from button
41+ BaseEvent originalEvent = uiPartActionEventItem . Evt ;
42+
43+ // Search for the BaseEventDelegate (BaseEvent.onEvent) field defined for the current BaseEvent type.
44+ // Note that 'onEvent' is protected, so we have to go through reflection.
45+ FieldInfo partEventFieldInfo = typeof ( BaseEvent ) . GetFields ( BindingFlags . Instance | BindingFlags . NonPublic )
46+ . First ( fi => fi . FieldType == typeof ( BaseEventDelegate ) ) ;
47+
48+ // Get the actual value of the 'onEvent' field
49+ BaseEventDelegate partEvent = ( BaseEventDelegate ) partEventFieldInfo . GetValue ( originalEvent ) ;
50+
51+ // Gets the method represented by the delegate and from this method returns an array of custom attributes applied to this member.
52+ // Simply put, we want all [KSPEvent] attributes applied to the BaseEventDelegate.Method field.
53+ object [ ] customAttributes = partEvent . Method . GetCustomAttributes ( typeof ( KSPEvent ) , true ) ;
54+
55+ // Look for the custom attribute skip_control
56+ bool skipControl = customAttributes . Any ( a => ( ( KSPEvent ) a ) . category . Contains ( "skip_control" ) ) ;
57+ if ( skipControl )
58+ continue ;
59+
60+ /*
61+ * Override the old BaseEvent with our BaseEvent to the button
62+ */
6463
65- // fix problems with other mods (behavior not seen with KSP) when the customAttributes list is empty.
66- KSPEvent kspEvent = customAttributes . Count ( ) == 0 ? WrappedEvent . KspEventFromBaseEvent ( originalEvent ) : ( KSPEvent ) customAttributes [ 0 ] ;
64+ // fix problems with other mods (behavior not seen with KSP) when the customAttributes list is empty.
65+ KSPEvent kspEvent = ! customAttributes . Any ( ) ? WrappedEvent . KspEventFromBaseEvent ( originalEvent ) : ( KSPEvent ) customAttributes [ 0 ] ;
6766
68- // Look for the custom attribute skip_delay
69- bool ignore_delay = customAttributes . Any ( a => ( ( KSPEvent ) a ) . category . Contains ( "skip_delay" ) ) ;
67+ // Look for the custom attribute skip_delay
68+ bool ignoreDelay = customAttributes . Any ( a => ( ( KSPEvent ) a ) . category . Contains ( "skip_delay" ) ) ;
7069
71- // create the new BaseEvent
72- BaseEvent hookedEvent = EventWrapper . CreateWrapper ( originalEvent , passthrough , ignore_delay , kspEvent ) ;
70+ // create the new BaseEvent
71+ BaseEvent hookedEvent = EventWrapper . CreateWrapper ( originalEvent , passthrough , ignoreDelay , kspEvent ) ;
7372
74- // get the original event index in the event list
75- BaseEventList eventList = originalEvent . listParent ;
76- int listIndex = eventList . IndexOf ( originalEvent ) ;
73+ // get the original event index in the event list
74+ BaseEventList eventList = originalEvent . listParent ;
75+ int listIndex = eventList . IndexOf ( originalEvent ) ;
7776
78- // remove the original event in the event list and add our hooked event
79- eventList . RemoveAt ( listIndex ) ;
80- eventList . Add ( hookedEvent ) ;
77+ // remove the original event in the event list and add our hooked event
78+ eventList . RemoveAt ( listIndex ) ;
79+ eventList . Add ( hookedEvent ) ;
8180
82- // get the baseEvent field from UIPartActionEventItem
83- FieldInfo baseEventField = typeof ( UIPartActionEventItem ) . GetFields ( BindingFlags . Instance | BindingFlags . NonPublic )
84- . First ( fi => fi . FieldType == typeof ( BaseEvent ) ) ;
81+ // get the baseEvent field from UIPartActionEventItem (note: this is uiPartActionEventItem.Evt, but we can't set its value...)
82+ FieldInfo baseEventField = typeof ( UIPartActionEventItem ) . GetFields ( BindingFlags . Instance | BindingFlags . NonPublic )
83+ . First ( fi => fi . FieldType == typeof ( BaseEvent ) ) ;
8584
86- // replace the button baseEvent value with our hooked event
87- baseEventField . SetValue ( button , hookedEvent ) ;
88- }
89- }
85+ // replace the button baseEvent value with our hooked event
86+ baseEventField . SetValue ( uiPartActionEventItem , hookedEvent ) ;
9087 }
9188 }
9289
93- public static void WrapPartAction ( Vessel parent , Action < BaseField , bool > passthrough )
90+ public static void WrapPartActionFieldItem ( Part part , Action < BaseField , bool > passthrough )
9491 {
9592 var controller = UIPartActionController . Instance ;
9693 if ( ! controller )
9794 return ;
9895
99- // Get the open context menus
100- var listFieldInfo = controller . GetType ( ) . GetFields ( BindingFlags . Instance | BindingFlags . NonPublic )
101- . First ( fi => fi . FieldType == typeof ( List < UIPartActionWindow > ) ) ;
96+ // get the part action window corresponding to the part
97+ var window = controller . GetItem ( part ) ;
98+ if ( window == null )
99+ return ;
102100
103- var openWindows = ( List < UIPartActionWindow > ) listFieldInfo . GetValue ( controller ) ;
101+ // get all the items that makes this window (toggle buttons, sliders, etc.)
102+ var partActionItems = window . ListItems ;
104103
105- foreach ( UIPartActionWindow window in openWindows . Where ( window => window . part . vessel == parent ) )
104+ // loop through all of those UI components
105+ for ( var i = 0 ; i < partActionItems . Count ( ) ; i ++ )
106106 {
107- // Get the list of all UIPartActionItem's
108- var itemsFieldInfo = typeof ( UIPartActionWindow ) . GetFields ( BindingFlags . Instance | BindingFlags . NonPublic )
109- . First ( fi => fi . FieldType == typeof ( List < UIPartActionItem > ) ) ;
110-
111- var partActionItems = ( List < UIPartActionItem > ) itemsFieldInfo . GetValue ( window ) ;
112-
113- var actionToogleButtons = partActionItems . Where ( l => AllowedFieldTypes . Any ( t => l . GetType ( ) == t ) && ( l as UIPartActionFieldItem ) != null ) . ToArray ( ) ;
114- for ( var i = 0 ; i < actionToogleButtons . Count ( ) ; i ++ )
115- {
116- var actionField = ( actionToogleButtons [ i ] as UIPartActionFieldItem ) ;
117- if ( actionField == null )
118- continue ;
119- /*if (parsedPartActions.Contains(actionField.Field.name))
120- continue;
121- else
122- parsedPartActions.Add(actionField.Field.name);*/
107+ // check that the part action item is actually a UIPartActionFieldItem (it could be a UIPartActionEventItem)
108+ var uiPartActionFieldItem = ( partActionItems [ i ] as UIPartActionFieldItem ) ;
109+ if ( uiPartActionFieldItem == null )
110+ continue ;
123111
124- var customAttributes = actionField . Field . FieldInfo . GetCustomAttributes ( typeof ( KSPField ) , true ) ;
112+ // now check that the UIPartActionFieldItem type (e.g UIPartActionToggle; UIPartActionCycle; UIPartActionFloatRange, etc.)
113+ // is actually something we can handle.
114+ if ( UIPartActionFieldItemAllowedTypes . All ( type => uiPartActionFieldItem . GetType ( ) != type ) )
115+ continue ;
125116
126- var kspField = ! customAttributes . Any ( ) ? WrappedField . KspFieldFromBaseField ( actionField . Field ) : ( KSPField ) customAttributes [ 0 ] ;
127-
128- var fieldWrapper = new FieldWrapper ( actionField , kspField , passthrough , false ) ;
129- }
117+ var fieldWrapper = new FieldWrapper ( uiPartActionFieldItem , passthrough , false ) ;
130118 }
119+
131120 }
132121
133122 #region FieldWrapper
@@ -201,20 +190,14 @@ public class FieldWrapper
201190 private Action < float > _delayInvoke ;
202191 private object _lastNewValue ;
203192
204- public FieldWrapper ( UIPartActionFieldItem uiPartAction , KSPField kspField , Action < BaseField , bool > passthrough , bool ignoreDelay )
193+ public FieldWrapper ( UIPartActionFieldItem uiPartAction , Action < BaseField , bool > passthrough , bool ignoreDelay )
205194 {
206195 _uiPartAction = uiPartAction ;
207196 SetDefaultListener ( ) ;
208-
209- var baseField = uiPartAction . Field ;
210- if ( kspField == null )
211- {
212- kspField = WrappedField . KspFieldFromBaseField ( baseField ) ;
213- }
214-
197+
215198 _passthrough = passthrough ;
216199 _ignoreDelay = ignoreDelay ;
217- _wrappedField = new WrappedField ( baseField , kspField ) ;
200+ _wrappedField = new WrappedField ( uiPartAction . Field , WrappedField . KspFieldFromBaseField ( uiPartAction . Field ) ) ;
218201 }
219202
220203 public void Invoke ( )
0 commit comments