diff --git a/BizHawk.Client.Common/BizHawk.Client.Common.csproj b/BizHawk.Client.Common/BizHawk.Client.Common.csproj
index 340fc01f08..d3766ece21 100644
--- a/BizHawk.Client.Common/BizHawk.Client.Common.csproj
+++ b/BizHawk.Client.Common/BizHawk.Client.Common.csproj
@@ -116,6 +116,7 @@
Code
+
diff --git a/BizHawk.Client.Common/inputAdapters/AutoPattern.cs b/BizHawk.Client.Common/inputAdapters/AutoPattern.cs
new file mode 100644
index 0000000000..ca63dcaea9
--- /dev/null
+++ b/BizHawk.Client.Common/inputAdapters/AutoPattern.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace BizHawk.Client.Common
+{
+ public class AutoPatternBool
+ {
+ public bool SkipsLag = true;
+ private bool[] _pattern;
+ private int _index;
+
+ ///
+ /// Autohold.
+ ///
+ public AutoPatternBool()
+ {
+ SkipsLag = true;
+ _index = 0;
+ _pattern = new bool[] { true };
+ }
+ ///
+ /// Simple on/off pattern.
+ ///
+ ///
+ ///
+ public AutoPatternBool(int on, int off, bool skip_lag = true, int offset = 0)
+ {
+ SkipsLag = skip_lag;
+ _index = offset;
+ _pattern = new bool[on + off];
+ for (int i = 0; i < on; i++)
+ _pattern[i] = true;
+ }
+ public AutoPatternBool(bool[] pattern, bool skip_lag = true, int offset = 0)
+ {
+ SkipsLag = skip_lag;
+ _pattern = pattern;
+ _index = offset;
+ }
+
+ ///
+ /// Gets the next value and increments index.
+ ///
+ ///
+ public bool GetNextValue()
+ {
+ bool ret = _pattern[_index];
+ _index++;
+ _index = _index % _pattern.Length;
+
+ return ret;
+ }
+
+ ///
+ /// Gets the next value without incrementing index.
+ ///
+ ///
+ public bool PeekNextValue()
+ { return _pattern[_index]; }
+ }
+
+ public class AutoPatternFloat
+ {
+ public bool SkipsLag = true;
+ private float[] _pattern;
+ private int _index;
+
+ ///
+ /// Defaults to 0.
+ ///
+ public AutoPatternFloat()
+ {
+ SkipsLag = true;
+ _pattern = new float[] { 0f };
+ _index = 0;
+ }
+ ///
+ /// Sinple on/off pattern, using the given values as on/off.
+ ///
+ public AutoPatternFloat(float valueOn, int on, float valueOff, int off, bool skip_lag = true, int offset = 0)
+ {
+ SkipsLag = skip_lag;
+ _index = offset;
+ _pattern = new float[on + off];
+ for (int i = 0; i < on; i++)
+ _pattern[i] = valueOn;
+ for (int i = on; i < _pattern.Length; i++)
+ _pattern[i] = valueOff;
+ }
+ public AutoPatternFloat(float[] pattern, bool skip_lag = true, int offset = 0)
+ {
+ SkipsLag = skip_lag;
+ _pattern = pattern;
+ _index = offset;
+ }
+
+ ///
+ /// Gets the next value and increments index.
+ ///
+ ///
+ public float GetNextValue()
+ {
+ float ret = _pattern[_index];
+ _index++;
+ _index = _index % _pattern.Length;
+
+ return ret;
+ }
+
+ ///
+ /// Gets the next value without incrementing index.
+ ///
+ ///
+ public float PeekNextValue()
+ { return _pattern[_index]; }
+ }
+}
diff --git a/BizHawk.Client.Common/inputAdapters/InputAdapters.cs b/BizHawk.Client.Common/inputAdapters/InputAdapters.cs
index 82b4fb3220..72e287fb27 100644
--- a/BizHawk.Client.Common/inputAdapters/InputAdapters.cs
+++ b/BizHawk.Client.Common/inputAdapters/InputAdapters.cs
@@ -16,7 +16,7 @@ namespace BizHawk.Client.Common
public class ClickyVirtualPadController : IController
{
public ControllerDefinition Type { get; set; }
-
+
public bool this[string button]
{
get { return IsPressed(button); }
@@ -32,7 +32,7 @@ namespace BizHawk.Client.Common
{
return _pressed.Contains(button);
}
-
+
///
/// call this once per frame to do the timekeeping for the hold and release
///
@@ -168,7 +168,8 @@ namespace BizHawk.Client.Common
public virtual bool this[string button]
{
- get { return Buttons[button]; } set { Buttons[button] = value; }
+ get { return Buttons[button]; }
+ set { Buttons[button] = value; }
}
public virtual bool IsPressed(string button)
@@ -242,12 +243,12 @@ namespace BizHawk.Client.Common
public class StickyXorAdapter : IController, ISticky
{
protected HashSet stickySet = new HashSet();
-
+
public IController Source { get; set; }
public ControllerDefinition Type
{
- get { return Source.Type; }
+ get { return Source.Type; }
set { throw new InvalidOperationException(); }
}
@@ -260,8 +261,8 @@ namespace BizHawk.Client.Common
// if SetFloat() is called (typically virtual pads), then that float will entirely override the Source input
// otherwise, the source is passed thru.
- private readonly WorkingDictionary _floatSet = new WorkingDictionary();
-
+ protected readonly WorkingDictionary _floatSet = new WorkingDictionary();
+
public void SetFloat(string name, float? value)
{
if (value.HasValue)
@@ -297,8 +298,8 @@ namespace BizHawk.Client.Common
}
public bool this[string button]
- {
- get
+ {
+ get
{
var source = Source[button];
source ^= stickySet.Contains(button);
@@ -382,138 +383,230 @@ namespace BizHawk.Client.Common
private List _justPressed = new List();
}
+ /// SuuperW: I'm leaving the old class in case I accidentally screwed something up.
+ //public class AutoFireStickyXorAdapter : IController, ISticky
+ //{
+ // public int On { get; set; }
+ // public int Off { get; set; }
+ // public WorkingDictionary buttonStarts = new WorkingDictionary();
+ // public WorkingDictionary lagStarts = new WorkingDictionary(); // TODO: need a data structure not misc dictionaries
+
+ // private readonly HashSet _stickySet = new HashSet();
+
+ // public IController Source { get; set; }
+
+ // public void SetOnOffPatternFromConfig()
+ // {
+ // On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn;
+ // Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff;
+ // }
+
+ // public AutoFireStickyXorAdapter()
+ // {
+ // //On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn;
+ // //Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff;
+ // On = 1;
+ // Off = 1;
+ // }
+
+ // public bool IsPressed(string button)
+ // {
+ // return this[button];
+ // }
+
+ // public bool this[string button]
+ // {
+ // get
+ // {
+ // var source = Source[button];
+
+ // if (_stickySet.Contains(button))
+ // {
+ // var lagcount = 0;
+ // if (Global.Emulator.CanPollInput() && Global.Config.AutofireLagFrames)
+ // {
+ // lagcount = Global.Emulator.AsInputPollable().LagCount;
+ // }
+
+ // var a = ((Global.Emulator.Frame - lagcount) - (buttonStarts[button] - lagStarts[button])) % (On + Off);
+ // if (a < On)
+ // {
+ // return source ^= true;
+ // }
+ // else
+ // {
+ // return source ^= false;
+ // }
+ // }
+
+ // return source;
+ // }
+
+ // set
+ // {
+ // throw new InvalidOperationException();
+ // }
+ // }
+
+ // public ControllerDefinition Type { get { return Source.Type; } set { throw new InvalidOperationException(); } }
+ // public bool Locked { get; set; } // Pretty much a hack,
+
+ // // dumb passthrough for floats, because autofire doesn't care about them
+ // public float GetFloat(string name)
+ // {
+ // return Source.GetFloat(name);
+ // }
+
+ // public void SetSticky(string button, bool isSticky)
+ // {
+ // if (isSticky)
+ // {
+ // _stickySet.Add(button);
+ // buttonStarts.Add(button, Global.Emulator.Frame);
+
+ // if (Global.Emulator.CanPollInput())
+ // {
+ // lagStarts.Add(button, Global.Emulator.AsInputPollable().LagCount);
+ // }
+ // else
+ // {
+ // lagStarts.Add(button, 0);
+ // }
+ // }
+ // else
+ // {
+ // _stickySet.Remove(button);
+ // buttonStarts.Remove(button);
+ // lagStarts.Remove(button);
+ // }
+ // }
+
+ // public bool IsSticky(string button)
+ // {
+ // return this._stickySet.Contains(button);
+ // }
+
+ // public HashSet CurrentStickies
+ // {
+ // get
+ // {
+ // return this._stickySet;
+ // }
+ // }
+
+ // public void ClearStickies()
+ // {
+ // _stickySet.Clear();
+ // buttonStarts.Clear();
+ // lagStarts.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;
+ // }
+
+ // ///
+ // /// 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 (Source.IsPressed(button))
+ // {
+ // return false;
+ // }
+
+ // return (IsPressed(button)); // Shortcut logic since we know the Source isn't pressed, Ispressed can only return true if the autofire sticky is in effect for this frame
+ // }
+
+ // private List _justPressed = new List();
+ //}
public class AutoFireStickyXorAdapter : IController, ISticky
{
- public int On { get; set; }
- public int Off { get; set; }
- public WorkingDictionary buttonStarts = new WorkingDictionary();
- public WorkingDictionary lagStarts = new WorkingDictionary(); // TODO: need a data structure not misc dictionaries
-
- private readonly HashSet _stickySet = new HashSet();
-
- public IController Source { get; set; }
-
+ 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 = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn;
- //Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff;
- On = 1;
- Off = 1;
+ On = 1; Off = 1;
}
+ public IController Source { get; set; }
+
+ public ControllerDefinition Type
+ {
+ get { return Source.Type; }
+ }
+
+ 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].GetNextValue();
+
+ if (Source == null)
+ return 0;
+
+ return Source.GetFloat(name);
+ }
+
+ public void ClearStickyFloats()
+ {
+ _floatPatterns.Clear();
+ }
+
public bool this[string button]
{
get
{
var source = Source[button];
-
- if (_stickySet.Contains(button))
- {
- var lagcount = 0;
- if (Global.Emulator.CanPollInput() && Global.Config.AutofireLagFrames)
- {
- lagcount = Global.Emulator.AsInputPollable().LagCount;
- }
-
- var a = ((Global.Emulator.Frame - lagcount) - (buttonStarts[button] - lagStarts[button])) % (On + Off);
- if (a < On)
- {
- return source ^= true;
- }
- else
- {
- return source ^= false;
- }
- }
-
+ bool patternValue = false;
+ if (_boolPatterns.ContainsKey(button))
+ patternValue = _boolPatterns[button].GetNextValue();
+ source ^= patternValue;
return source;
}
-
- set
- {
- throw new InvalidOperationException();
- }
- }
-
- public ControllerDefinition Type { get { return Source.Type; } set { throw new InvalidOperationException(); } }
- public bool Locked { get; set; } // Pretty much a hack,
-
- // dumb passthrough for floats, because autofire doesn't care about them
- public float GetFloat(string name)
- {
- return Source.GetFloat(name);
- }
-
- public void SetSticky(string button, bool isSticky)
- {
- if (isSticky)
- {
- _stickySet.Add(button);
- buttonStarts.Add(button, Global.Emulator.Frame);
-
- if (Global.Emulator.CanPollInput())
- {
- lagStarts.Add(button, Global.Emulator.AsInputPollable().LagCount);
- }
- else
- {
- lagStarts.Add(button, 0);
- }
- }
- else
- {
- _stickySet.Remove(button);
- buttonStarts.Remove(button);
- lagStarts.Remove(button);
- }
- }
-
- public bool IsSticky(string button)
- {
- return this._stickySet.Contains(button);
- }
-
- public HashSet CurrentStickies
- {
- get
- {
- return this._stickySet;
- }
- }
-
- public void ClearStickies()
- {
- _stickySet.Clear();
- buttonStarts.Clear();
- lagStarts.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;
}
///
@@ -522,15 +615,74 @@ namespace BizHawk.Client.Common
///
public bool StickyIsInEffect(string button)
{
- if (Source.IsPressed(button))
+ if (IsSticky(button))
{
- return false;
+ return !Source.IsPressed(button);
}
- return (IsPressed(button)); // Shortcut logic since we know the Source isn't pressed, Ispressed can only return true if the autofire sticky is in effect for this frame
+ 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();
+ }
+
+ // SuuperW: What does this even do? I set a breakpoint inside the loop and it wasn't reached.
+ private WorkingDictionary _toggledButtons = new WorkingDictionary();
private List _justPressed = new List();
+ public void MassToggleStickyState(List buttons)
+ {
+ foreach (var button in buttons.Where(button => !_justPressed.Contains(button)))
+ {
+ if (_boolPatterns.ContainsKey(button))
+ {
+ _toggledButtons[button] = _boolPatterns[button];
+ SetSticky(button, false);
+ }
+ else
+ {
+ _boolPatterns[button] = _toggledButtons[button];
+ _toggledButtons.Remove(button);
+ }
+ }
+
+ _justPressed = buttons;
+ }
}
///
@@ -539,7 +691,7 @@ namespace BizHawk.Client.Common
public class CopyControllerAdapter : IController
{
public IController Source { get; set; }
-
+
private readonly NullController _null = new NullController();
private IController Curr