using System; using System.Collections.Generic; using System.Text; using System.Xml; using System.Linq; using System.IO; using SCJMapper_V2.Common; using SCJMapper_V2.SC; using SCJMapper_V2.Devices; using SCJMapper_V2.Devices.Keyboard; using SCJMapper_V2.Devices.Mouse; using SCJMapper_V2.Devices.Gamepad; using SCJMapper_V2.Devices.Joystick; using System.Xml.Linq; namespace SCJMapper_V2.Actions { /// /// Maintains an action - something like: /// /// /// /// /// /// /// AC1.0 /// /// /// /// /// /// AC1.1 /// /// /// /// /// AC2.0 /// /// // jsN, moN, kbN (gamepad ?) /// // jsN, moN, kbN (gamepad ?) still possible together with rebind? /// /// /// public class ActionCls { private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod( ).DeclaringType ); public string Key { get; set; } // the key is the "Daction" formatted item (as we can have the same name multiple times) public string ActionName { get; set; } // the plain action name e.g. v_yaw public Act.ActionDevice ActionDevice { get; set; } // the enum of the device public string Device { get; set; } // name of the device (uses DeviceClass) public string DefBinding { get; set; } // the default binding public ActivationMode DefActivationMode { get; set; } // the default binding ActivationMode public List InputList { get; set; } // regular bind is the 0-element, addbinds are added to the list /// /// Clone this object /// /// A deep Clone of this object private ActionCls MyClone() { ActionCls newAc = (ActionCls)this.MemberwiseClone( ); // more objects to deep copy newAc.DefActivationMode = (ActivationMode)this.DefActivationMode.Clone( ); newAc.InputList = this.InputList.Select( x => (ActionCommandCls)x.Clone( ) ).ToList( ); return newAc; } /// /// Copy return the action while reassigning the JsN Tag /// /// The JsN reassign list /// The action copy with reassigned input public ActionCls ReassignJsN( JsReassingList newJsList ) { ActionCls newAc = this.MyClone( ); // creates a copy of the list with reassigned jsN devs newAc.InputList.Clear( ); // get rid of cloned list foreach ( ActionCommandCls acc in InputList ) { newAc.InputList.Add( acc.ReassignJsN( newJsList ) ); // creates the deep copy of the list } return newAc; } /// /// ctor /// public ActionCls() { Key = ""; ActionDevice = Act.ActionDevice.AD_Unknown; Device = JoystickCls.DeviceClass; ActionName = ""; DefBinding = ""; DefActivationMode = ActivationMode.Default; InputList = new List( ); // empty list } /// /// Creates and adds the inputCommand list with given input string /// AC2 style input is used i.e. with device tag in front /// apply given ActivationMode - can be "~" to indicate DONT APPLY /// /// /// Returns the ActionCommand created public ActionCommandCls AddCommand( string devInput, ActivationMode activationMode ) { ActionCommandCls acc = new ActionCommandCls( devInput, InputList.Count - 1 ); // starts from -1 ... acc.ActivationMode = new ActivationMode( activationMode ); InputList.Add( acc ); return acc; } /// /// Add an ActionCommand with Input at nodeindex /// apply default ActivationMode /// /// The input to apply /// The nodeindex /// The created ActionCommand public ActionCommandCls AddCommand( string devInput, int index ) { ActionCommandCls acc = new ActionCommandCls( devInput, index ) { ActivationMode = new ActivationMode( ActivationMode.Default ) }; InputList.Add( acc ); return acc; } /// /// Delete an ActionCommand with nodeindex /// /// The nodeindex public void DelCommand( int index ) { int removeIt = -1; for ( int i = 0; i < InputList.Count; i++ ) { if ( InputList[i].NodeIndex == index ) removeIt = i; if ( InputList[i].NodeIndex > index ) InputList[i].NodeIndex -= 1; // reorder trailing ones } if ( removeIt >= 0 ) InputList.RemoveAt( removeIt ); } /// /// Merge action is simply copying the new input control /// /// public void Merge( ActionCls newAc ) { this.InputList.Clear( ); foreach ( ActionCommandCls acc in newAc.InputList ) { this.InputList.Add( acc ); } } /// /// Updates an actionCommand with a new input (command) /// /// The input command /// The input index to update public void UpdateCommandFromInput( string devInput, int accIndex ) // ActionCommandCls actionCmd ) { //log.Debug( "UpdateCommandFromInput - Entry" ); if ( accIndex < 0 ) return; // Apply the input to the ActionTree this.InputList[accIndex].DevInput = Act.DisableInput( devInput, this.ActionDevice ); if ( Act.IsDisabledInput( this.InputList[accIndex].DevInput ) || string.IsNullOrEmpty( devInput ) ) { this.InputList[accIndex].ActivationMode = new ActivationMode( ActivationMode.Default ); // reset activation mode if the input is empty } } /// /// Find an ActionCommand with input in an Action /// /// The input /// An actionCommand or null if not found public ActionCommandCls FindActionInputObject( string devInput ) { log.Debug( "FindActionInputObject - Entry" ); // Apply the input to the ActionTree ActionCommandCls acc = null; acc = this.InputList.Find( delegate ( ActionCommandCls _ACC ) { return _ACC.DevInput == devInput; } ); if ( acc == null ) { log.Error( "FindActionInputObject - Action Input not found in Action" ); return null; // ERROR - Action Input not found in tree } return acc; } /// /// Find an ActionCommand with index in an Action /// /// The input index to find /// An actionCommand or null if not found public ActionCommandCls FindActionInputObject( int index ) { log.Debug( "FindActionInputObject - Entry" ); // Apply the input to the ActionTree ActionCommandCls acc = null; acc = this.InputList.Find( delegate ( ActionCommandCls _ACC ) { return _ACC.NodeIndex == index; } ); if ( acc == null ) { log.Error( "FindActionInputObject - Action Input not found in Action" ); return null; // ERROR - Action Input not found in tree } return acc; } /// /// Dump the action as partial XML nicely formatted /// /// the action as XML fragment public string toXML() { string r = ""; string bindCmd = "rebind"; // first entry is rebind if ( InputList.Count > 0 ) { if ( !string.IsNullOrEmpty( InputList[0].Input ) ) { r = string.Format( "\t\n", ActionName ); foreach ( ActionCommandCls acc in InputList ) { if ( !string.IsNullOrEmpty( acc.Input ) ) { r += string.Format( "\t\t\t<{0} {1}", bindCmd, acc.toXML( ) ); // 20151220BM: format for AC2 style bindCmd = "addbind"; // switch to addbind } } r += string.Format( "\t\t\n" ); } } return r; } /// /// Read an action from XML - do some sanity checks /// /// the XML action fragment /// True if an action was decoded public bool fromXML( XElement actionNode ) { ActionName = (string)actionNode.Attribute( "name" ); // mandadory foreach ( XElement bindingNode in actionNode.Nodes( ) ) { string binding = bindingNode.Name.ToString( ); string input = "", actModeName = "", multi = ""; input = (string)bindingNode.Attribute( "input" ); // mandadory if ( string.IsNullOrEmpty( input ) ) input = ""; actModeName = (string)bindingNode.Attribute( "ActivationMode" ); multi = (string)bindingNode.Attribute( "multiTap" ); string device = (string)bindingNode.Attribute( "device" ); //process input = DeviceCls.fromXML( input ); // move from external to internal blend if ( !string.IsNullOrEmpty( device ) ) { // AC1 style - need to reformat mouse and keyboard according to AC2 style now if ( KeyboardCls.IsDeviceClass( device ) ) input = KeyboardCls.FromAC1( input ); else if ( MouseCls.IsDeviceClass( device ) ) input = MouseCls.FromAC1( input ); else if ( GamepadCls.IsDeviceClass( device ) ) input = GamepadCls.FromAC1( input ); } Device = Act.DeviceClassFromInput( input ); ActivationMode actMode = null; if ( !string.IsNullOrEmpty( actModeName ) ) { actMode = ActivationModes.Instance.ActivationModeByName( actModeName ); // should be a valid ActivationMode for this action } else { actMode = new ActivationMode( ActivationMode.Default ); // no specific name given, use default if ( !string.IsNullOrEmpty( multi ) ) { actMode.MultiTap = int.Parse( multi ); // modify with given multiTap } } if ( binding == "rebind" ) { Key = Act.DevTag( Device ) + ActionName; // unique id of the action ActionDevice = Act.ADevice( Device ); // get the enum of the input device } AddCommand( input, actMode ); }//foreach return true; } } }