using System; using System.Collections.Generic; using System.Text; using System.Xml; using System.IO; namespace SCJMapper_V2 { /// /// 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 enum ActionDevice { AD_Unknown = -1, AD_Joystick = 0, AD_Gamepad, AD_Keyboard, AD_Mouse, // 20151220BM: add mouse device (from AC 2.0 defaultProfile usage) } #region Static Items static public ActionDevice ADevice( String device ) { switch ( device.ToLower( ) ) { case KeyboardCls.DeviceClass: return ActionDevice.AD_Keyboard; case JoystickCls.DeviceClass: return ActionDevice.AD_Joystick; case GamepadCls.DeviceClass: return ActionDevice.AD_Gamepad; case MouseCls.DeviceClass: return ActionDevice.AD_Mouse; // 20151220BM: add mouse device (from AC 2.0 defaultProfile usage) case "ps3pad": return ActionDevice.AD_Gamepad; default: return ActionDevice.AD_Unknown; } } // Static items to have this mapping in only one place /// /// Returns the Device ID i.e. the single letter to tag a device /// /// The device name from the CryFile /// The single UCase device ID letter static public String DevID( String device ) { switch ( device.ToLower( ) ) { case KeyboardCls.DeviceClass: return "K"; case JoystickCls.DeviceClass: return "J"; case GamepadCls.DeviceClass: return "X"; case MouseCls.DeviceClass: return "M"; // 20151220BM: add mouse device (from AC 2.0 defaultProfile usage) case "ps3pad": return "P"; default: return "Z"; } } /// /// Returns the Device name from the ID /// /// The single UCase device ID letter /// The device name from the CryFile static public String DeviceFromID( String devID ) { switch ( devID ) { case "K": return KeyboardCls.DeviceClass; case "J": return JoystickCls.DeviceClass; case "X": return GamepadCls.DeviceClass; case "M": return MouseCls.DeviceClass; // 20151220BM: add mouse device (from AC 2.0 defaultProfile usage) case "P": return "ps3pad"; default: return "unknown"; } } /// /// Try to derive the device class from the input string /// /// The input command string /// A proper DeviceClass string static public String DeviceClassFromInput( String input ) { String deviceClass = DeviceCls.DeviceClass; deviceClass = JoystickCls.DeviceClassFromInput( input ); if ( !DeviceCls.IsUndefined( deviceClass ) ) return deviceClass; deviceClass = GamepadCls.DeviceClassFromInput( input ); if ( !DeviceCls.IsUndefined( deviceClass ) ) return deviceClass; deviceClass = KeyboardCls.DeviceClassFromInput( input ); if ( !DeviceCls.IsUndefined( deviceClass ) ) return deviceClass; deviceClass = MouseCls.DeviceClassFromInput( input ); if ( !DeviceCls.IsUndefined( deviceClass ) ) return deviceClass; // others.. return deviceClass; } /// /// Query the devices if the input is blended /// /// The input command /// True if blended input static public Boolean IsBlendedInput( String input ) { Boolean blendedInput = false; blendedInput = JoystickCls.IsBlendedInput( input ); if ( blendedInput ) return blendedInput; blendedInput = GamepadCls.IsBlendedInput( input ); if ( blendedInput ) return blendedInput; blendedInput = KeyboardCls.IsBlendedInput( input ); if ( blendedInput ) return blendedInput; blendedInput = MouseCls.IsBlendedInput( input ); if ( blendedInput ) return blendedInput; // others.. return blendedInput; } static public String BlendInput( String input, ActionDevice aDevice ) { if ( DeviceCls.IsBlendedInput( input ) ) { switch ( aDevice ) { case ActionDevice.AD_Gamepad: return GamepadCls.BlendedInput; case ActionDevice.AD_Joystick: return JoystickCls.BlendedInput; case ActionDevice.AD_Keyboard: return KeyboardCls.BlendedInput; case ActionDevice.AD_Mouse: return MouseCls.BlendedInput; default: return ""; } } else { return input; // just return } } #endregion // **************** Class items ********************** public String key { get; set; } // the key is the "Daction" formatted item (as we can have the same name multiple times) public String name { get; set; } // the plain action name e.g. v_yaw public 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 String defActivationMode { get; set; } // the default binding ActivationMode (can be "" if not used) public List inputList { get; set; } // regular bind is the 0-element, addbinds are added to the list /// /// ctor /// public ActionCls( ) { key = ""; actionDevice = ActionDevice.AD_Unknown; device = JoystickCls.DeviceClass; name = ""; defBinding = ""; defActivationMode = ""; inputList = new List( ); // empty list } /// /// 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 = new ActionCls( ); // full copy from 'this' newAc.key = this.key; newAc.actionDevice = this.actionDevice; newAc.device = this.device; newAc.name = this.name; newAc.defBinding = this.defBinding; newAc.defActivationMode = this.defActivationMode; // creates a copy of the list with reassigned jsN devs foreach ( ActionCommandCls acc in inputList ) { newAc.inputList.Add( acc.ReassignJsN( newJsList ) ); } return newAc; } /// /// Created 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, String activationMode ) { ActionCommandCls acc = new ActionCommandCls( devInput, inputList.Count - 1 ); // starts from -1 ... if ( activationMode == "~" ) { // not assigned acc.ActivationMode = ""; } else { acc.ActivationMode = activationMode; } inputList.Add( acc ); return acc; } /// /// Add an ActionCommand with Input at nodeindex /// apply default ActivationMode /// /// /// /// public ActionCommandCls AddCommand( String devInput, int index ) { ActionCommandCls acc = new ActionCommandCls( devInput, index ); acc.ActivationMode = defActivationMode; inputList.Add( acc ); return acc; } 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 actionCommand to update public void UpdateCommandFromInput( String devInput, ActionCommandCls actionCmd ) { //log.Debug( "UpdateCommandFromInput - Entry" ); if ( actionCmd == null ) return; // Apply the input to the ActionTree actionCmd.DevInput = BlendInput( devInput, this.actionDevice ); if ( string.IsNullOrEmpty( actionCmd.Input)) { actionCmd.ActivationMode = defActivationMode; // 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 /// 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"; if ( inputList.Count > 0 ) { if ( !String.IsNullOrEmpty( inputList[0].Input ) ) { r = String.Format( "\t\n", name ); foreach ( ActionCommandCls acc in inputList ) { if ( !String.IsNullOrEmpty( acc.Input ) ) { // r += String.Format( "\t\t\t<{0} device=\"{1}\" {2}", bindCmd, device, acc.toXML( ) ); // OLD style 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 check /// /// the XML action fragment /// True if an action was decoded public Boolean fromXML( String xml ) { XmlReaderSettings settings = new XmlReaderSettings( ); settings.ConformanceLevel = ConformanceLevel.Fragment; settings.IgnoreWhitespace = true; settings.IgnoreComments = true; XmlReader reader = XmlReader.Create( new StringReader( xml ), settings ); reader.Read( ); if ( reader.Name.ToLowerInvariant( ) == "action" ) { if ( reader.HasAttributes ) { name = reader["name"]; reader.ReadStartElement( "action" ); // Checks that the current content node is an element with the given Name and advances the reader to the next node } else { return false; } } do { // support AC2 and AC1 i.e. without and with device attribute if ( reader.Name.ToLowerInvariant( ) == "rebind" ) { if ( reader.HasAttributes ) { device = reader["device"]; String input = reader["input"]; if ( String.IsNullOrEmpty( input ) ) return false; // ERROR exit input = DeviceCls.fromXML( input ); // move from external to internal blend if ( String.IsNullOrEmpty( device ) ) { // AC2 style - derive the device (Device.DeviceClass) device = DeviceClassFromInput( input ); } else { // 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 ); } // Get default ActivationMode String activationMode = reader["ActivationMode"]; if ( string.IsNullOrEmpty(activationMode)) activationMode = ActivationModes.Default; // MARK AS NOT USED key = DevID( device ) + name; // unique id of the action actionDevice = ADevice( device ); // get the enum of the input device AddCommand( input, activationMode ); // advances the reader to the next node reader.ReadStartElement( "rebind" ); } } else if ( reader.Name.ToLowerInvariant( ) == "addbind" ) { if ( reader.HasAttributes ) { device = reader["device"]; String input = reader["input"]; if ( String.IsNullOrEmpty( input ) ) return false; // ERROR exit input = DeviceCls.fromXML( input ); // move from external to internal blend if ( String.IsNullOrEmpty( device ) ) { // AC2 style - derive the device (Device.DeviceClass) device = DeviceClassFromInput( input ); } else { // AC1 style - need to reformat 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 ); } String activationMode = reader["ActivationMode"]; if ( string.IsNullOrEmpty( activationMode ) ) activationMode = ActivationModes.Default; // MARK AS NOT USED AddCommand( input, activationMode ); // advances the reader to the next node reader.ReadStartElement( "addbind" ); } } else { return false; } } while ( reader.Name == "addbind" ); return true; } } }