using System; using System.Text; using System.Collections.Generic; namespace BizHawk.MultiClient { /// /// will hold buttons for 1 frame and then release them. (Calling Click() from your button click is what you want to do) /// TODO - should the duration be controllable? /// public class ClickyVirtualPadController : IController { public ControllerDefinition Type { get; set; } public bool this[string button] { get { return IsPressed(button); } } public float GetFloat(string name) { return 0.0f; } //TODO public void UpdateControls(int frame) { } public bool IsPressed(string button) { return Pressed.Contains(button); } /// /// call this once per frame to do the timekeeping for the hold and release /// public void FrameTick() { Pressed.Clear(); } /// /// call this to hold the button down for one frame /// public void Click(string button) { Pressed.Add(button); } HashSet Pressed = new HashSet(); } //filters input for things called Up and Down while considering the client's AllowUD_LR option. //this is a bit gross but it is unclear how to do it more nicely public class UD_LR_ControllerAdapter : IController { public ControllerDefinition Type { get { return Source.Type; } } public IController Source; public bool this[string button] { get { return IsPressed(button); } } public float GetFloat(string name) { return 0.0f; } //TODO public void UpdateControls(int frame) { } public bool IsPressed(string button) { if (Global.Config.AllowUD_LR == true) return Source.IsPressed(button); string prefix; if (button.Contains("Down")) { prefix = button.GetPrecedingString("Down"); if (Source.IsPressed(prefix + "Up")) return false; } if (button.Contains("Right")) { prefix = button.GetPrecedingString("Right"); if (Source.IsPressed(prefix + "Left")) return false; } return Source.IsPressed(button); } } public class SimpleController : IController { public ControllerDefinition Type { get; set; } protected WorkingDictionary Buttons = new WorkingDictionary(); public virtual bool this[string button] { get { return Buttons[button]; } set { Buttons[button] = value; } } public virtual bool IsPressed(string button) { return this[button]; } public float GetFloat(string name) { return 0.0f; } //TODO public void UpdateControls(int frame) { } public IEnumerable> BoolButtons() { foreach (var kvp in Buttons) yield return kvp; } public virtual void LatchFrom(IController source) { foreach (string button in source.Type.BoolButtons) { Buttons[button] = source[button]; } } } public class ORAdapter : IController { public bool IsPressed(string button) { return this[button]; } public float GetFloat(string name) { return 0.0f; } //TODO public void UpdateControls(int frame) { } public IController Source; public IController SourceOr; public ControllerDefinition Type { get { return Source.Type; } set { throw new InvalidOperationException(); } } public bool this[string button] { get { bool source = Source[button] | SourceOr[button]; return source; } set { throw new InvalidOperationException(); } } } public class StickyXORAdapter : IController { private HashSet stickySet = new HashSet(); public IController Source; public ControllerDefinition Type { get { return Source.Type; } set { throw new InvalidOperationException(); } } public bool IsPressed(string button) { return this[button]; } public float GetFloat(string name) { return 0.0f; } //TODO public void UpdateControls(int frame) { } public bool this[string button] { get { bool source = Source[button]; if (source) { } source ^= stickySet.Contains(button); return source; } set { throw new InvalidOperationException(); } } public void SetSticky(string button, bool isSticky) { if(isSticky) stickySet.Add(button); else stickySet.Remove(button); } public bool IsSticky(string button) { return stickySet.Contains(button); } } public class MnemonicsGenerator { IController Source; public void SetSource(IController source) { Source = source; ControlType = source.Type.Name; } string ControlType; bool IsBasePressed(string name) { bool ret = Source.IsPressed(name); if (ret) { //int zzz=9; } return ret; } public string GetControllersAsMnemonic() { if (Global.Emulator.SystemId == "NULL" || ControlType == "Null Controller") return "|.|"; StringBuilder input = new StringBuilder("|"); if ( ControlType == "Genesis 3-Button Controller" || ControlType == "Gameboy Controller" || ControlType == "PC Engine Controller" ) { input.Append("."); } if (ControlType == "Atari 2600 Basic Controller") { input.Append(IsBasePressed("Reset") ? "r" : "."); input.Append(IsBasePressed("Select") ? "s" : "."); } if (ControlType == "NES Controller") { input.Append(IsBasePressed("Reset") ? Global.COMMANDS[ControlType]["Reset"] : Global.Emulator.IsLagFrame ? Global.COMMANDS[ControlType]["Lag"] : "."); } if (ControlType != "SMS Controller" && ControlType != "TI83 Controller") { input.Append("|"); } for (int player = 1; player <= Global.PLAYERS[ControlType]; player++) { string prefix = ""; if (ControlType != "Gameboy Controller" && ControlType != "TI83 Controller") { prefix = "P" + player.ToString() + " "; } foreach (string button in Global.BUTTONS[ControlType].Keys) { input.Append(IsBasePressed(prefix + button) ? Global.BUTTONS[ControlType][button] : "."); } input.Append("|"); } if (ControlType == "SMS Controller") { foreach (string command in Global.COMMANDS[ControlType].Keys) { input.Append(IsBasePressed(command) ? Global.COMMANDS[ControlType][command] : "."); } input.Append("|"); } if (ControlType == "TI83 Controller") { input.Append(".|"); //TODO: perhaps ON should go here? } return input.ToString(); } } /// /// just copies source to sink, or returns whatever a NullController would if it is disconnected. useful for immovable hardpoints. /// public class CopyControllerAdapter : IController { public IController Source; NullController _null = new NullController(); IController Curr { get { if (Source == null) return _null; else return Source; } } public ControllerDefinition Type { get { return Curr.Type; } } public bool this[string button] { get { return Curr[button]; } } public bool IsPressed(string button) { return Curr.IsPressed(button); } public float GetFloat(string name) { return Curr.GetFloat(name); } public void UpdateControls(int frame) { Curr.UpdateControls(frame); } } class ButtonNameParser { ButtonNameParser() { } public static ButtonNameParser Parse(string button) { //see if we're being asked for a button that we know how to rewire string[] parts = button.Split(' '); if (parts.Length < 2) return null; if (parts[0][0] != 'P') return null; int player = 0; if (!int.TryParse(parts[0].Substring(1), out player)) return null; var bnp = new ButtonNameParser(); bnp.PlayerNum = player; bnp.ButtonPart = button.Substring(parts[0].Length + 1); return bnp; } public int PlayerNum; public string ButtonPart; public override string ToString() { return string.Format("P{0} {1}", PlayerNum, ButtonPart); } } /// /// rewires player1 controls to playerN /// public class MultitrackRewiringControllerAdapter : IController { public IController Source; public int PlayerSource = 1; public int PlayerTargetMask = 0; public ControllerDefinition Type { get { return Source.Type; } } public bool this[string button] { get { return this.IsPressed(button); } } public float GetFloat(string name) { return Source.GetFloat(name); } public void UpdateControls(int frame) { Source.UpdateControls(frame); } public bool IsPressed(string button) { //do we even have a source? if (PlayerSource == -1) return Source.IsPressed(button); //see if we're being asked for a button that we know how to rewire ButtonNameParser bnp = ButtonNameParser.Parse(button); if (bnp == null) return Source.IsPressed(button); //ok, this looks like a normal `P1 Button` type thing. we can handle it //were we supposed to replace this one? int foundPlayerMask = (1 << bnp.PlayerNum); if ((PlayerTargetMask & foundPlayerMask) == 0) return Source.IsPressed(button); //ok, we were. swap out the source player and then grab his button bnp.PlayerNum = PlayerSource; return Source.IsPressed(bnp.ToString()); } } public class MovieControllerAdapter : IController { public MovieControllerAdapter() { //OutputController = new ForceControllerAdapter(); } //IController implementation: public ControllerDefinition Type { get; set; } public bool this[string button] { get { return MyBoolButtons[button]; } } public bool IsPressed(string button) { return MyBoolButtons[button]; } public float GetFloat(string name) { return 0; } public void UpdateControls(int frame) { } //-------- WorkingDictionary MyBoolButtons = new WorkingDictionary(); void Force(string button, bool state) { MyBoolButtons[button] = state; } string ControlType { get { return Type.Name; } } class MnemonicChecker { public MnemonicChecker(string _m) { m = _m; } string m; public bool this[int c] { get { return m[c] != '.'; } } } /// /// latches one player from the source /// public void LatchPlayerFromSource(IController playerSource, int playerNum) { foreach (string button in playerSource.Type.BoolButtons) { ButtonNameParser bnp = ButtonNameParser.Parse(button); if (bnp == null) continue; if (bnp.PlayerNum != playerNum) continue; bool val = playerSource[button]; MyBoolButtons[button] = val; } } /// /// latches all buttons from the provided source /// public void LatchFromSource(IController source) { foreach (string button in Type.BoolButtons) { MyBoolButtons[button] = source[button]; } } /// /// latches all buttons from the supplied mnemonic string /// public void SetControllersAsMnemonic(string mnemonic) { if (Global.Emulator.SystemId == "NULL" || ControlType == "Null Controller") return; MnemonicChecker c = new MnemonicChecker(mnemonic); MyBoolButtons.Clear(); int start = 3; if (ControlType == "NES Controller") { if (mnemonic.Length < 2) return; Force("Reset", mnemonic[1] != '.' && mnemonic[1] != '0' && mnemonic[1] != 'l'); } if (ControlType == "SMS Controller" || ControlType == "TI83 Controller") { start = 1; } if (ControlType == "Atari 2600 Basic Controller") { Force("Reset", mnemonic[1] != '.' && mnemonic[1] != '0'); Force("Select", mnemonic[2] != '.' && mnemonic[2] != '0'); start = 4; } for (int player = 1; player <= Global.PLAYERS[ControlType]; player++) { int srcindex = (player - 1) * (Global.BUTTONS[ControlType].Count + 1); int ctr = start; if (mnemonic.Length < srcindex + ctr + Global.BUTTONS[ControlType].Count - 1) { return; } string prefix = ""; if (ControlType != "Gameboy Controller" && ControlType != "TI83 Controller") { prefix = "P" + player + " "; } foreach (string button in Global.BUTTONS[ControlType].Keys) { Force(prefix + button, c[srcindex + ctr++]); } } if (ControlType == "SMS Controller") { int srcindex = Global.PLAYERS[ControlType] * (Global.BUTTONS[ControlType].Count + 1); int ctr = start; foreach (string command in Global.COMMANDS[ControlType].Keys) { Force(command, c[srcindex + ctr++]); } } } } //not being used.. ///// ///// adapts an IController to force some buttons to a different state. ///// unforced button states will flow through to the adaptee ///// //public class ForceControllerAdapter : IController //{ // public IController Controller; // public Dictionary Forces = new Dictionary(); // public void Clear() // { // Forces.Clear(); // } // public ControllerDefinition Type { get { return Controller.Type; } } // public bool this[string button] { get { return IsPressed(button); } } // public bool IsPressed(string button) // { // if (Forces.ContainsKey(button)) // return Forces[button]; // else return Controller.IsPressed(button); // } // public float GetFloat(string name) // { // return Controller.GetFloat(name); //TODO! // } // public void UpdateControls(int frame) // { // Controller.UpdateControls(frame); // } //} }