From a955e6a3a2a6f35603734e3b8cb4dc10bc6642a7 Mon Sep 17 00:00:00 2001 From: adelikat Date: Wed, 14 Dec 2016 13:12:37 -0600 Subject: [PATCH] Break up InputAdapters into separate files because it was getting unwieldy --- .../BizHawk.Client.Common.csproj | 7 +- .../ClickyVirtualPadController.cs | 78 ++ .../inputAdapters/CopyController.cs | 42 + .../inputAdapters/InputAdapters.cs | 740 ------------------ .../inputAdapters/OverrideAdaptor.cs | 129 +++ .../inputAdapters/SimpleController.cs | 62 ++ .../inputAdapters/StickyAdapters.cs | 351 +++++++++ .../inputAdapters/UDLRController.cs | 169 ++++ 8 files changed, 837 insertions(+), 741 deletions(-) create mode 100644 BizHawk.Client.Common/inputAdapters/ClickyVirtualPadController.cs create mode 100644 BizHawk.Client.Common/inputAdapters/CopyController.cs delete mode 100644 BizHawk.Client.Common/inputAdapters/InputAdapters.cs create mode 100644 BizHawk.Client.Common/inputAdapters/OverrideAdaptor.cs create mode 100644 BizHawk.Client.Common/inputAdapters/SimpleController.cs create mode 100644 BizHawk.Client.Common/inputAdapters/StickyAdapters.cs create mode 100644 BizHawk.Client.Common/inputAdapters/UDLRController.cs diff --git a/BizHawk.Client.Common/BizHawk.Client.Common.csproj b/BizHawk.Client.Common/BizHawk.Client.Common.csproj index 610d55229a..38882005b7 100644 --- a/BizHawk.Client.Common/BizHawk.Client.Common.csproj +++ b/BizHawk.Client.Common/BizHawk.Client.Common.csproj @@ -154,9 +154,14 @@ + + - + + + + diff --git a/BizHawk.Client.Common/inputAdapters/ClickyVirtualPadController.cs b/BizHawk.Client.Common/inputAdapters/ClickyVirtualPadController.cs new file mode 100644 index 0000000000..a08750f40a --- /dev/null +++ b/BizHawk.Client.Common/inputAdapters/ClickyVirtualPadController.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using BizHawk.Emulation.Common; + +namespace BizHawk.Client.Common +{ + /// + /// 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 Definition { get; set; } + + public bool this[string button] + { + get { return IsPressed(button); } + } + + public bool IsPressed(string button) + { + return _pressed.Contains(button); + } + + public float GetFloat(string name) + { + return 0.0f; + } + + /// + /// 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); + } + + public void Unclick(string button) + { + _pressed.Remove(button); + } + + public void Toggle(string button) + { + if (IsPressed(button)) + { + _pressed.Remove(button); + } + else + { + _pressed.Add(button); + } + } + + public void SetBool(string button, bool value) + { + if (value) + { + _pressed.Remove(button); + } + else + { + _pressed.Add(button); + } + } + + private readonly HashSet _pressed = new HashSet(); + } + +} diff --git a/BizHawk.Client.Common/inputAdapters/CopyController.cs b/BizHawk.Client.Common/inputAdapters/CopyController.cs new file mode 100644 index 0000000000..827317434b --- /dev/null +++ b/BizHawk.Client.Common/inputAdapters/CopyController.cs @@ -0,0 +1,42 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Client.Common +{ + /// + /// 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 { get; set; } + + private IController Curr + { + get + { + return Source == null + ? NullController.Instance + : Source; + } + } + + public ControllerDefinition Definition + { + get { return Curr.Definition; } + } + + 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); + } + } +} diff --git a/BizHawk.Client.Common/inputAdapters/InputAdapters.cs b/BizHawk.Client.Common/inputAdapters/InputAdapters.cs deleted file mode 100644 index 1c40baf36b..0000000000 --- a/BizHawk.Client.Common/inputAdapters/InputAdapters.cs +++ /dev/null @@ -1,740 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using BizHawk.Common; -using BizHawk.Common.StringExtensions; -using BizHawk.Emulation.Common; - -namespace BizHawk.Client.Common -{ - /// - /// 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 Definition { get; set; } - - public bool this[string button] - { - get { return IsPressed(button); } - } - - public float GetFloat(string name) - { - return 0.0f; - } - - // TODO - 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); - } - - public void Unclick(string button) - { - _pressed.Remove(button); - } - - public void Toggle(string button) - { - if (IsPressed(button)) - { - _pressed.Remove(button); - } - else - { - _pressed.Add(button); - } - } - - public void SetBool(string button, bool value) - { - if (value) - { - _pressed.Remove(button); - } - else - { - _pressed.Add(button); - } - } - - private readonly 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 Definition - { - get { return Source.Definition; } - } - - public bool this[string button] - { - get { return IsPressed(button); } - } - - public IController Source { get; set; } - - // The float format implies no U+D and no L+R no matter what, so just passthru - public float GetFloat(string name) - { - return Source.GetFloat(name); - } - - HashSet Unpresses = new HashSet(); - - public bool IsPressed(string button) - { - bool PriorityUD_LR = !Global.Config.AllowUD_LR && !Global.Config.ForbidUD_LR; //implied by neither of the others being set (left as non-enum for back-compatibility) - - - if (Global.Config.AllowUD_LR) - { - return Source.IsPressed(button); - } - - string prefix; - - //" C " is for N64 "P1 C Up" and the like, which should not be subject to mutexing - - //regarding the unpressing and UDLR logic...... don't think about it. don't question it. don't look at it. - - if (button.Contains("Down") && !button.Contains(" C ")) - { - if (!Source.IsPressed(button)) Unpresses.Remove(button); - prefix = button.GetPrecedingString("Down"); - string other = prefix + "Up"; - if (Source.IsPressed(other)) - { - if (Unpresses.Contains(button)) return false; - if (Global.Config.ForbidUD_LR) return false; - Unpresses.Add(other); - } - else Unpresses.Remove(button); - } - - if (button.Contains("Up") && !button.Contains(" C ")) - { - if (!Source.IsPressed(button)) Unpresses.Remove(button); - prefix = button.GetPrecedingString("Up"); - string other = prefix + "Down"; - if (Source.IsPressed(other)) - { - if (Unpresses.Contains(button)) return false; - if (Global.Config.ForbidUD_LR) return false; - Unpresses.Add(other); - } - else Unpresses.Remove(button); - } - - - if (button.Contains("Right") && !button.Contains(" C ")) - { - if (!Source.IsPressed(button)) Unpresses.Remove(button); - prefix = button.GetPrecedingString("Right"); - string other = prefix + "Left"; - if (Source.IsPressed(other)) - { - if (Unpresses.Contains(button)) return false; - if (Global.Config.ForbidUD_LR) return false; - Unpresses.Add(other); - } - else Unpresses.Remove(button); - } - - if (button.Contains("Left") && !button.Contains(" C ")) - { - if (!Source.IsPressed(button)) Unpresses.Remove(button); - prefix = button.GetPrecedingString("Left"); - string other = prefix + "Right"; - if (Source.IsPressed(other)) - { - if (Unpresses.Contains(button)) return false; - if (Global.Config.ForbidUD_LR) return false; - Unpresses.Add(other); - } - else Unpresses.Remove(button); - } - - return Source.IsPressed(button); - } - } - - public class SimpleController : IController - { - public ControllerDefinition Definition { get; set; } - - protected WorkingDictionary Buttons = new WorkingDictionary(); - protected WorkingDictionary Floats = new WorkingDictionary(); - - public virtual void Clear() - { - Buttons = new WorkingDictionary(); - Floats = 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 Floats[name]; - } - - public IEnumerable> BoolButtons() - { - return Buttons; - } - - public virtual void LatchFrom(IController source) - { - foreach (var button in source.Definition.BoolButtons) - { - Buttons[button] = source.IsPressed(button); - } - } - - public void AcceptNewFloats(IEnumerable> newValues) - { - foreach (var sv in newValues) - { - Floats[sv.Item1] = sv.Item2; - } - } - } - - // Used by input display, to determine if either autofire or regular stickies are "in effect" because we color this scenario differently - public class StickyOrAdapter : IController - { - public bool IsPressed(string button) - { - return this[button]; - } - - // pass floats solely from the original source - // this works in the code because SourceOr is the autofire controller - public float GetFloat(string name) - { - int i = Source.Definition.FloatControls.IndexOf(name); - return Source.Definition.FloatRanges[i].Mid; // Floats don't make sense in sticky land - } - - public ISticky Source { get; set; } - public ISticky SourceStickyOr { get; set; } - public ControllerDefinition Definition { get { return Source.Definition; } set { throw new InvalidOperationException(); } } - - public bool this[string button] - { - get - { - return Source.StickyIsInEffect(button) || - SourceStickyOr.StickyIsInEffect(button); - } - - set - { - throw new InvalidOperationException(); - } - } - } - - public interface ISticky : IController - { - bool StickyIsInEffect(string button); - } - - public class StickyXorAdapter : IController, ISticky - { - protected HashSet stickySet = new HashSet(); - - public IController Source { get; set; } - - public ControllerDefinition Definition - { - get { return Source.Definition; } - set { throw new InvalidOperationException(); } - } - - public bool Locked { get; set; } // Pretty much a hack, - - public bool IsPressed(string button) - { - return this[button]; - } - - // if SetFloat() is called (typically virtual pads), then that float will entirely override the Source input - // otherwise, the source is passed thru. - protected readonly WorkingDictionary _floatSet = new WorkingDictionary(); - - public void SetFloat(string name, float? value) - { - if (value.HasValue) - { - _floatSet[name] = value; - } - else - { - _floatSet.Remove(name); - } - } - - public float GetFloat(string name) - { - var val = _floatSet[name]; - - if (val.HasValue) - { - return val.Value; - } - - if (Source == null) - { - return 0; - } - - return Source.GetFloat(name); - } - - public void ClearStickyFloats() - { - _floatSet.Clear(); - } - - public bool this[string button] - { - get - { - var source = Source[button]; - source ^= stickySet.Contains(button); - return source; - } - - set - { - throw new InvalidOperationException(); - } - } - - /// - /// Determines if a sticky is current mashing the button itself, - /// If sticky is not set then false, if set, it returns true if the Source is not pressed, else false - /// - public bool StickyIsInEffect(string button) - { - if (IsSticky(button)) - { - return !Source.IsPressed(button); - } - - return false; - } - - public void SetSticky(string button, bool isSticky) - { - if (isSticky) - { - stickySet.Add(button); - } - else - { - stickySet.Remove(button); - } - } - - public void Unset(string button) - { - stickySet.Remove(button); - _floatSet.Remove(button); - } - - public bool IsSticky(string button) - { - return stickySet.Contains(button); - } - - public HashSet CurrentStickies - { - get - { - return stickySet; - } - } - - public void ClearStickies() - { - stickySet.Clear(); - _floatSet.Clear(); - } - - public void MassToggleStickyState(List buttons) - { - foreach (var button in buttons.Where(button => !_justPressed.Contains(button))) - { - if (stickySet.Contains(button)) - { - stickySet.Remove(button); - } - else - { - stickySet.Add(button); - } - } - - _justPressed = buttons; - } - - private List _justPressed = new List(); - } - - // commenting this out, it breaks the autofire hotkey - public class AutoFireStickyXorAdapter : IController, ISticky - { - // TODO: Change the AutoHold adapter to be one of these, with an 'Off' value of 0? - // Probably would have slightly lower performance, but it seems weird to have such a similar class that is only used once. - private int On; - private int Off; - public void SetOnOffPatternFromConfig() - { - On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn; - Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff; - } - - private WorkingDictionary _boolPatterns = new WorkingDictionary(); - private WorkingDictionary _floatPatterns = new WorkingDictionary(); - - public AutoFireStickyXorAdapter() - { - On = 1; Off = 1; - } - - public IController Source { get; set; } - - public ControllerDefinition Definition - { - get { return Source.Definition; } - } - - public bool Locked { get; set; } // Pretty much a hack, - - public bool IsPressed(string button) - { - return this[button]; - } - - public void SetFloat(string name, float? value, AutoPatternFloat pattern = null) - { - if (value.HasValue) - { - if (pattern == null) - pattern = new AutoPatternFloat(value.Value, On, 0, Off); - _floatPatterns[name] = pattern; - } - else - { - _floatPatterns.Remove(name); - } - } - - public float GetFloat(string name) - { - if (_floatPatterns.ContainsKey(name)) - return _floatPatterns[name].PeekNextValue(); - - if (Source == null) - return 0; - - return Source.GetFloat(name); - } - - public void ClearStickyFloats() - { - _floatPatterns.Clear(); - } - - public bool this[string button] - { - get - { - var source = Source[button]; - bool patternValue = false; - if (_boolPatterns.ContainsKey(button)) - { // I can't figure a way to determine right here if it should Peek or Get. - patternValue = _boolPatterns[button].PeekNextValue(); - } - source ^= patternValue; - - return source; - } - } - - /// - /// Determines if a sticky is current mashing the button itself, - /// If sticky is not set then false, if set, it returns true if the Source is not pressed, else false - /// - public bool StickyIsInEffect(string button) - { - if (IsSticky(button)) - { - return !Source.IsPressed(button); - } - - return false; - } - - public void SetSticky(string button, bool isSticky, AutoPatternBool pattern = null) - { - if (isSticky) - { - if (pattern == null) - pattern = new AutoPatternBool(On, Off); - _boolPatterns[button] = pattern; - } - else - { - _boolPatterns.Remove(button); - } - } - - public void Unset(string button) - { - _boolPatterns.Remove(button); - _floatPatterns.Remove(button); - } - - public bool IsSticky(string button) - { - return _boolPatterns.ContainsKey(button) || _floatPatterns.ContainsKey(button); - } - - public HashSet CurrentStickies - { - get - { - return new HashSet(_boolPatterns.Keys); - } - } - - public void ClearStickies() - { - _boolPatterns.Clear(); - _floatPatterns.Clear(); - } - - public void IncrementLoops(bool lagged) - { - for (int i = 0; i < _boolPatterns.Count; i++) - _boolPatterns.ElementAt(i).Value.GetNextValue(lagged); - for (int i = 0; i < _floatPatterns.Count; i++) - _floatPatterns.ElementAt(i).Value.GetNextValue(lagged); - } - - private List _justPressed = new List(); - public void MassToggleStickyState(List buttons) - { - foreach (var button in buttons.Where(button => !_justPressed.Contains(button))) - { - if (_boolPatterns.ContainsKey(button)) - SetSticky(button, false); - else - SetSticky(button, true); - } - - _justPressed = buttons; - } - } - - /// - /// 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 { get; set; } - - private readonly NullController _null = new NullController(); - - private IController Curr - { - get - { - if (Source == null) - { - return _null; - } - else - { - return Source; - } - } - } - - public ControllerDefinition Definition - { - get { return Curr.Definition; } - } - - 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); - } - } - - /// - /// Used to pass into an Override method to manage the logic overriding input - /// This only works with bool buttons! - /// - public class OverrideAdaptor : IController - { - private readonly Dictionary _overrides = new Dictionary(); - private readonly Dictionary _floatOverrides = new Dictionary(); - private readonly List _inverses = new List(); - - public bool this[string button] - { - get - { - if (_overrides.ContainsKey(button)) - { - return _overrides[button]; - } - - throw new InvalidOperationException(); - } - - set - { - if (_overrides.ContainsKey(button)) - { - _overrides[button] = value; - } - else - { - _overrides.Add(button, value); - } - } - } - - public ControllerDefinition Definition { get; set; } - - public IEnumerable Overrides - { - get - { - foreach (var kvp in _overrides) - { - yield return kvp.Key; - } - } - } - - public IEnumerable FloatOverrides - { - get - { - foreach (var kvp in _floatOverrides) - { - yield return kvp.Key; - } - } - } - - public IEnumerable InversedButtons - { - get - { - foreach (var name in _inverses) - { - yield return name; - } - } - } - - public void SetFloat(string name, float value) - { - if (_floatOverrides.ContainsKey(name)) - { - _floatOverrides[name] = value; - } - else - { - _floatOverrides.Add(name, value); - } - } - - public float GetFloat(string name) - { - if (_floatOverrides.ContainsKey(name)) - { - return _floatOverrides[name]; - } - - return 0.0F; - } - - public bool IsPressed(string button) { return this[button]; } - - public void SetButton(string button, bool value) - { - this[button] = value; - _inverses.Remove(button); - } - - public void UnSet(string button) - { - _overrides.Remove(button); - _inverses.Remove(button); - } - - public void SetInverse(string button) - { - _inverses.Add(button); - } - - public void FrameTick() - { - _overrides.Clear(); - _floatOverrides.Clear(); - _inverses.Clear(); - } - } -} \ No newline at end of file diff --git a/BizHawk.Client.Common/inputAdapters/OverrideAdaptor.cs b/BizHawk.Client.Common/inputAdapters/OverrideAdaptor.cs new file mode 100644 index 0000000000..3fa79580d0 --- /dev/null +++ b/BizHawk.Client.Common/inputAdapters/OverrideAdaptor.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Client.Common +{ + /// + /// Used to pass into an Override method to manage the logic overriding input + /// This only works with bool buttons! + /// + public class OverrideAdaptor : IController + { + private readonly Dictionary _overrides = new Dictionary(); + private readonly Dictionary _floatOverrides = new Dictionary(); + private readonly List _inverses = new List(); + + public ControllerDefinition Definition { get; set; } + + public bool this[string button] + { + get + { + if (_overrides.ContainsKey(button)) + { + return _overrides[button]; + } + + throw new InvalidOperationException(); + } + + set + { + if (_overrides.ContainsKey(button)) + { + _overrides[button] = value; + } + else + { + _overrides.Add(button, value); + } + } + } + + public bool IsPressed(string button) + { + return this[button]; + } + + public float GetFloat(string name) + { + if (_floatOverrides.ContainsKey(name)) + { + return _floatOverrides[name]; + } + + return 0.0F; + } + + public IEnumerable Overrides + { + get + { + foreach (var kvp in _overrides) + { + yield return kvp.Key; + } + } + } + + public IEnumerable FloatOverrides + { + get + { + foreach (var kvp in _floatOverrides) + { + yield return kvp.Key; + } + } + } + + public IEnumerable InversedButtons + { + get + { + foreach (var name in _inverses) + { + yield return name; + } + } + } + + public void SetFloat(string name, float value) + { + if (_floatOverrides.ContainsKey(name)) + { + _floatOverrides[name] = value; + } + else + { + _floatOverrides.Add(name, value); + } + } + + public void SetButton(string button, bool value) + { + this[button] = value; + _inverses.Remove(button); + } + + public void UnSet(string button) + { + _overrides.Remove(button); + _inverses.Remove(button); + } + + public void SetInverse(string button) + { + _inverses.Add(button); + } + + public void FrameTick() + { + _overrides.Clear(); + _floatOverrides.Clear(); + _inverses.Clear(); + } + } +} diff --git a/BizHawk.Client.Common/inputAdapters/SimpleController.cs b/BizHawk.Client.Common/inputAdapters/SimpleController.cs new file mode 100644 index 0000000000..12d19d83c1 --- /dev/null +++ b/BizHawk.Client.Common/inputAdapters/SimpleController.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Client.Common +{ + /// + /// A basic implementation of IController + /// + public class SimpleController : IController + { + public ControllerDefinition Definition { get; set; } + + protected WorkingDictionary Buttons = new WorkingDictionary(); + protected WorkingDictionary Floats = new WorkingDictionary(); + + public virtual void Clear() + { + Buttons = new WorkingDictionary(); + Floats = 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 Floats[name]; + } + + public IEnumerable> BoolButtons() + { + return Buttons; + } + + public virtual void LatchFrom(IController source) + { + foreach (var button in source.Definition.BoolButtons) + { + Buttons[button] = source.IsPressed(button); + } + } + + public void AcceptNewFloats(IEnumerable> newValues) + { + foreach (var sv in newValues) + { + Floats[sv.Item1] = sv.Item2; + } + } + } +} diff --git a/BizHawk.Client.Common/inputAdapters/StickyAdapters.cs b/BizHawk.Client.Common/inputAdapters/StickyAdapters.cs new file mode 100644 index 0000000000..bac0d72493 --- /dev/null +++ b/BizHawk.Client.Common/inputAdapters/StickyAdapters.cs @@ -0,0 +1,351 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Client.Common +{ + public interface ISticky : IController + { + bool StickyIsInEffect(string button); + } + + /// + /// Used by input display, to determine if either autofire or regular stickies + /// are "in effect" because we color this scenario differently + /// + public class StickyOrAdapter : IController + { + public bool IsPressed(string button) + { + return this[button]; + } + + // pass floats solely from the original source + // this works in the code because SourceOr is the autofire controller + public float GetFloat(string name) + { + int i = Source.Definition.FloatControls.IndexOf(name); + return Source.Definition.FloatRanges[i].Mid; // Floats don't make sense in sticky land + } + + public ISticky Source { get; set; } + public ISticky SourceStickyOr { get; set; } + public ControllerDefinition Definition { get { return Source.Definition; } set { throw new InvalidOperationException(); } } + + public bool this[string button] + { + get + { + return Source.StickyIsInEffect(button) || + SourceStickyOr.StickyIsInEffect(button); + } + + set + { + throw new InvalidOperationException(); + } + } + } + + public class StickyXorAdapter : ISticky, IController + { + protected HashSet stickySet = new HashSet(); + + public IController Source { get; set; } + + public ControllerDefinition Definition + { + get { return Source.Definition; } + set { throw new InvalidOperationException(); } + } + + public bool Locked { get; set; } // Pretty much a hack, + + public bool IsPressed(string button) + { + return this[button]; + } + + // if SetFloat() is called (typically virtual pads), then that float will entirely override the Source input + // otherwise, the source is passed thru. + protected readonly WorkingDictionary _floatSet = new WorkingDictionary(); + + public void SetFloat(string name, float? value) + { + if (value.HasValue) + { + _floatSet[name] = value; + } + else + { + _floatSet.Remove(name); + } + } + + public float GetFloat(string name) + { + var val = _floatSet[name]; + + if (val.HasValue) + { + return val.Value; + } + + if (Source == null) + { + return 0; + } + + return Source.GetFloat(name); + } + + public void ClearStickyFloats() + { + _floatSet.Clear(); + } + + public bool this[string button] + { + get + { + var source = Source[button]; + source ^= stickySet.Contains(button); + return source; + } + + set + { + throw new InvalidOperationException(); + } + } + + /// + /// Determines if a sticky is current mashing the button itself, + /// If sticky is not set then false, if set, it returns true if the Source is not pressed, else false + /// + public bool StickyIsInEffect(string button) + { + if (IsSticky(button)) + { + return !Source.IsPressed(button); + } + + return false; + } + + public void SetSticky(string button, bool isSticky) + { + if (isSticky) + { + stickySet.Add(button); + } + else + { + stickySet.Remove(button); + } + } + + public void Unset(string button) + { + stickySet.Remove(button); + _floatSet.Remove(button); + } + + public bool IsSticky(string button) + { + return stickySet.Contains(button); + } + + public HashSet CurrentStickies + { + get + { + return stickySet; + } + } + + public void ClearStickies() + { + stickySet.Clear(); + _floatSet.Clear(); + } + + public void MassToggleStickyState(List buttons) + { + foreach (var button in buttons.Where(button => !_justPressed.Contains(button))) + { + if (stickySet.Contains(button)) + { + stickySet.Remove(button); + } + else + { + stickySet.Add(button); + } + } + + _justPressed = buttons; + } + + private List _justPressed = new List(); + } + + public class AutoFireStickyXorAdapter : ISticky, IController + { + // TODO: Change the AutoHold adapter to be one of these, with an 'Off' value of 0? + // Probably would have slightly lower performance, but it seems weird to have such a similar class that is only used once. + private int On; + private int Off; + public void SetOnOffPatternFromConfig() + { + On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn; + Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff; + } + + private WorkingDictionary _boolPatterns = new WorkingDictionary(); + private WorkingDictionary _floatPatterns = new WorkingDictionary(); + + public AutoFireStickyXorAdapter() + { + On = 1; Off = 1; + } + + public IController Source { get; set; } + + public ControllerDefinition Definition + { + get { return Source.Definition; } + } + + public bool Locked { get; set; } // Pretty much a hack, + + public bool IsPressed(string button) + { + return this[button]; + } + + public void SetFloat(string name, float? value, AutoPatternFloat pattern = null) + { + if (value.HasValue) + { + if (pattern == null) + pattern = new AutoPatternFloat(value.Value, On, 0, Off); + _floatPatterns[name] = pattern; + } + else + { + _floatPatterns.Remove(name); + } + } + + public float GetFloat(string name) + { + if (_floatPatterns.ContainsKey(name)) + return _floatPatterns[name].PeekNextValue(); + + if (Source == null) + return 0; + + return Source.GetFloat(name); + } + + public void ClearStickyFloats() + { + _floatPatterns.Clear(); + } + + public bool this[string button] + { + get + { + var source = Source[button]; + bool patternValue = false; + if (_boolPatterns.ContainsKey(button)) + { // I can't figure a way to determine right here if it should Peek or Get. + patternValue = _boolPatterns[button].PeekNextValue(); + } + source ^= patternValue; + + return source; + } + } + + /// + /// Determines if a sticky is current mashing the button itself, + /// If sticky is not set then false, if set, it returns true if the Source is not pressed, else false + /// + public bool StickyIsInEffect(string button) + { + if (IsSticky(button)) + { + return !Source.IsPressed(button); + } + + return false; + } + + public void SetSticky(string button, bool isSticky, AutoPatternBool pattern = null) + { + if (isSticky) + { + if (pattern == null) + pattern = new AutoPatternBool(On, Off); + _boolPatterns[button] = pattern; + } + else + { + _boolPatterns.Remove(button); + } + } + + public void Unset(string button) + { + _boolPatterns.Remove(button); + _floatPatterns.Remove(button); + } + + public bool IsSticky(string button) + { + return _boolPatterns.ContainsKey(button) || _floatPatterns.ContainsKey(button); + } + + public HashSet CurrentStickies + { + get + { + return new HashSet(_boolPatterns.Keys); + } + } + + public void ClearStickies() + { + _boolPatterns.Clear(); + _floatPatterns.Clear(); + } + + public void IncrementLoops(bool lagged) + { + for (int i = 0; i < _boolPatterns.Count; i++) + _boolPatterns.ElementAt(i).Value.GetNextValue(lagged); + for (int i = 0; i < _floatPatterns.Count; i++) + _floatPatterns.ElementAt(i).Value.GetNextValue(lagged); + } + + private List _justPressed = new List(); + public void MassToggleStickyState(List buttons) + { + foreach (var button in buttons.Where(button => !_justPressed.Contains(button))) + { + if (_boolPatterns.ContainsKey(button)) + SetSticky(button, false); + else + SetSticky(button, true); + } + + _justPressed = buttons; + } + } +} \ No newline at end of file diff --git a/BizHawk.Client.Common/inputAdapters/UDLRController.cs b/BizHawk.Client.Common/inputAdapters/UDLRController.cs new file mode 100644 index 0000000000..7a39f914b1 --- /dev/null +++ b/BizHawk.Client.Common/inputAdapters/UDLRController.cs @@ -0,0 +1,169 @@ +using System.Collections.Generic; + +using BizHawk.Common.StringExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Client.Common +{ + /// + /// 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 + { + private HashSet Unpresses = new HashSet(); + + public ControllerDefinition Definition + { + get { return Source.Definition; } + } + + public bool this[string button] + { + get { return IsPressed(button); } + } + + public IController Source { get; set; } + + // The float format implies no U+D and no L+R no matter what, so just passthru + public float GetFloat(string name) + { + return Source.GetFloat(name); + } + + public bool IsPressed(string button) + { + bool PriorityUD_LR = !Global.Config.AllowUD_LR && !Global.Config.ForbidUD_LR; // implied by neither of the others being set (left as non-enum for back-compatibility) + + + if (Global.Config.AllowUD_LR) + { + return Source.IsPressed(button); + } + + string prefix; + + //" C " is for N64 "P1 C Up" and the like, which should not be subject to mutexing + + //regarding the unpressing and UDLR logic...... don't think about it. don't question it. don't look at it. + + if (button.Contains("Down") && !button.Contains(" C ")) + { + if (!Source.IsPressed(button)) + { + Unpresses.Remove(button); + } + + prefix = button.GetPrecedingString("Down"); + string other = prefix + "Up"; + if (Source.IsPressed(other)) + { + if (Unpresses.Contains(button)) + { + return false; + } + + if (Global.Config.ForbidUD_LR) + { + return false; + } + + Unpresses.Add(other); + } + else + { + Unpresses.Remove(button); + } + } + + if (button.Contains("Up") && !button.Contains(" C ")) + { + if (!Source.IsPressed(button)) + { + Unpresses.Remove(button); + } + + prefix = button.GetPrecedingString("Up"); + string other = prefix + "Down"; + if (Source.IsPressed(other)) + { + if (Unpresses.Contains(button)) + { + return false; + } + + if (Global.Config.ForbidUD_LR) + { + return false; + } + + Unpresses.Add(other); + } + else + { + Unpresses.Remove(button); + } + } + + if (button.Contains("Right") && !button.Contains(" C ")) + { + if (!Source.IsPressed(button)) + { + Unpresses.Remove(button); + } + + prefix = button.GetPrecedingString("Right"); + string other = prefix + "Left"; + if (Source.IsPressed(other)) + { + if (Unpresses.Contains(button)) + { + return false; + } + + if (Global.Config.ForbidUD_LR) + { + return false; + } + + Unpresses.Add(other); + } + else + { + Unpresses.Remove(button); + } + } + + if (button.Contains("Left") && !button.Contains(" C ")) + { + if (!Source.IsPressed(button)) + { + Unpresses.Remove(button); + } + + prefix = button.GetPrecedingString("Left"); + string other = prefix + "Right"; + if (Source.IsPressed(other)) + { + if (Unpresses.Contains(button)) + { + return false; + } + + if (Global.Config.ForbidUD_LR) + { + return false; + } + + Unpresses.Add(other); + } + else + { + Unpresses.Remove(button); + } + } + + return Source.IsPressed(button); + } + } +}