From 0e34b36fbc4052896ffe3525eadf482820afbca4 Mon Sep 17 00:00:00 2001 From: SuuperW Date: Wed, 4 Mar 2015 21:04:31 +0000 Subject: [PATCH] -Added AutoFire patterns, changed AutoFireStickyXorAdapter to use them. -Bugfix: Painted input after the movie was recorded over. --- .../BizHawk.Client.Common.csproj | 1 + .../inputAdapters/AutoPattern.cs | 119 ++++++ .../inputAdapters/InputAdapters.cs | 394 ++++++++++++------ 3 files changed, 393 insertions(+), 121 deletions(-) create mode 100644 BizHawk.Client.Common/inputAdapters/AutoPattern.cs 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