2013-10-25 00:59:34 +00:00
using System ;
using System.Collections.Generic ;
2014-06-26 19:07:17 +00:00
using System.Linq ;
2014-07-03 15:05:02 +00:00
2013-10-27 22:07:40 +00:00
using BizHawk.Common ;
2014-07-03 15:05:02 +00:00
using BizHawk.Common.StringExtensions ;
2013-11-04 01:39:19 +00:00
using BizHawk.Emulation.Common ;
2013-10-27 22:07:40 +00:00
2014-07-03 15:16:47 +00:00
2013-10-25 00:59:34 +00:00
namespace BizHawk.Client.Common
{
/// <summary>
/// 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?
/// </summary>
public class ClickyVirtualPadController : IController
{
public ControllerDefinition Type { get ; set ; }
2013-12-07 00:53:06 +00:00
public bool this [ string button ]
{
get { return IsPressed ( button ) ; }
}
public float GetFloat ( string name )
{
return 0.0f ;
}
// TODO
2013-10-25 00:59:34 +00:00
public bool IsPressed ( string button )
{
2013-12-07 00:53:06 +00:00
return _pressed . Contains ( button ) ;
2013-10-25 00:59:34 +00:00
}
2013-12-07 00:53:06 +00:00
2013-10-25 00:59:34 +00:00
/// <summary>
/// call this once per frame to do the timekeeping for the hold and release
/// </summary>
public void FrameTick ( )
{
2013-12-07 00:53:06 +00:00
_pressed . Clear ( ) ;
2013-10-25 00:59:34 +00:00
}
/// <summary>
/// call this to hold the button down for one frame
/// </summary>
public void Click ( string button )
{
2013-12-07 00:53:06 +00:00
_pressed . Add ( button ) ;
2013-10-25 00:59:34 +00:00
}
public void Unclick ( string button )
{
2013-12-07 00:53:06 +00:00
_pressed . Remove ( button ) ;
2013-10-25 00:59:34 +00:00
}
public void Toggle ( string button )
{
if ( IsPressed ( button ) )
{
2013-12-07 00:53:06 +00:00
_pressed . Remove ( button ) ;
2013-10-25 00:59:34 +00:00
}
else
{
2013-12-07 00:53:06 +00:00
_pressed . Add ( button ) ;
2013-10-25 00:59:34 +00:00
}
}
2014-07-16 23:04:56 +00:00
public void SetBool ( string button , bool value )
{
if ( value )
{
_pressed . Remove ( button ) ;
}
else
{
_pressed . Add ( button ) ;
}
}
2013-12-07 00:53:06 +00:00
private readonly HashSet < string > _pressed = new HashSet < string > ( ) ;
2013-10-25 00:59:34 +00:00
}
2013-12-07 00:53:06 +00:00
/// <summary>
/// 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
/// </summary>
2013-10-25 00:59:34 +00:00
public class UD_LR_ControllerAdapter : IController
{
2013-12-07 00:53:06 +00:00
public ControllerDefinition Type
{
get { return Source . Type ; }
}
2013-10-25 00:59:34 +00:00
2013-12-07 00:53:06 +00:00
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 ) ;
}
2013-10-25 00:59:34 +00:00
public bool IsPressed ( string button )
{
if ( Global . Config . AllowUD_LR )
{
return Source . IsPressed ( button ) ;
}
string prefix ;
2014-09-14 18:01:20 +00:00
//" C " is for N64 "P1 C Up" and the like, which should not be subject to mutexing
2014-09-14 01:43:42 +00:00
2013-10-25 00:59:34 +00:00
if ( button . Contains ( "Down" ) & & ! button . Contains ( " C " ) )
{
prefix = button . GetPrecedingString ( "Down" ) ;
if ( Source . IsPressed ( prefix + "Up" ) )
return false ;
}
2013-12-07 00:53:06 +00:00
2014-09-14 01:43:42 +00:00
if ( button . Contains ( "Up" ) & & ! button . Contains ( " C " ) )
{
prefix = button . GetPrecedingString ( "Up" ) ;
if ( Source . IsPressed ( prefix + "Down" ) )
return false ;
}
2013-10-25 00:59:34 +00:00
if ( button . Contains ( "Right" ) & & ! button . Contains ( " C " ) )
{
prefix = button . GetPrecedingString ( "Right" ) ;
if ( Source . IsPressed ( prefix + "Left" ) )
{
return false ;
}
}
2014-09-14 01:43:42 +00:00
if ( button . Contains ( "Left" ) & & ! button . Contains ( " C " ) )
{
prefix = button . GetPrecedingString ( "Left" ) ;
if ( Source . IsPressed ( prefix + "Right" ) )
{
return false ;
}
}
2013-10-25 00:59:34 +00:00
return Source . IsPressed ( button ) ;
}
}
public class SimpleController : IController
{
public ControllerDefinition Type { get ; set ; }
protected WorkingDictionary < string , bool > Buttons = new WorkingDictionary < string , bool > ( ) ;
protected WorkingDictionary < string , float > Floats = new WorkingDictionary < string , float > ( ) ;
2013-12-24 21:37:51 +00:00
public virtual void Clear ( )
{
Buttons = new WorkingDictionary < string , bool > ( ) ;
Floats = new WorkingDictionary < string , float > ( ) ;
}
2013-12-07 00:53:06 +00:00
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 ] ;
}
2013-10-25 00:59:34 +00:00
public IEnumerable < KeyValuePair < string , bool > > BoolButtons ( )
{
2013-10-27 17:47:54 +00:00
return Buttons ;
2013-10-25 00:59:34 +00:00
}
public virtual void LatchFrom ( IController source )
{
2013-12-07 00:53:06 +00:00
foreach ( var button in source . Type . BoolButtons )
2013-10-25 00:59:34 +00:00
{
Buttons [ button ] = source [ button ] ;
}
}
2013-12-07 00:53:06 +00:00
public void AcceptNewFloats ( IEnumerable < Tuple < string , float > > newValues )
2013-10-25 00:59:34 +00:00
{
2013-12-07 00:53:06 +00:00
foreach ( var sv in newValues )
{
2013-10-25 00:59:34 +00:00
Floats [ sv . Item1 ] = sv . Item2 ;
2013-12-07 00:53:06 +00:00
}
2013-10-25 00:59:34 +00:00
}
}
2014-04-28 00:39:40 +00:00
// 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 ) { return 0.0F ; } // Floats don't make sense in sticky land
public ISticky Source { get ; set ; }
public ISticky SourceStickyOr { get ; set ; }
public ControllerDefinition Type { get { return Source . Type ; } 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
2013-10-25 00:59:34 +00:00
{
protected HashSet < string > stickySet = new HashSet < string > ( ) ;
2013-12-07 00:53:06 +00:00
public IController Source { get ; set ; }
2013-10-25 00:59:34 +00:00
2013-12-07 00:53:06 +00:00
public ControllerDefinition Type
{
get { return Source . Type ; }
set { throw new InvalidOperationException ( ) ; }
}
public bool Locked { get ; set ; } // Pretty much a hack,
2013-10-25 00:59:34 +00:00
2014-04-28 00:39:40 +00:00
public bool IsPressed ( string button )
{
return this [ button ] ;
}
2013-10-25 00:59:34 +00:00
// if SetFloat() is called (typically virtual pads), then that float will entirely override the Source input
// otherwise, the source is passed thru.
2013-12-07 00:53:06 +00:00
private readonly WorkingDictionary < string , float? > _floatSet = new WorkingDictionary < string , float? > ( ) ;
2013-10-25 00:59:34 +00:00
public void SetFloat ( string name , float? value )
{
if ( value . HasValue )
2013-12-07 00:53:06 +00:00
{
_floatSet [ name ] = value ;
}
else
{
_floatSet . Remove ( name ) ;
}
2013-10-25 00:59:34 +00:00
}
2013-12-07 00:53:06 +00:00
2013-10-25 00:59:34 +00:00
public float GetFloat ( string name )
{
2014-06-19 21:55:15 +00:00
var val = _floatSet [ name ] ;
if ( val . HasValue )
{
return val . Value ;
}
if ( Source = = null )
{
return 0 ;
}
return Source . GetFloat ( name ) ;
2013-10-25 00:59:34 +00:00
}
2013-12-07 00:53:06 +00:00
2013-10-25 00:59:34 +00:00
public void ClearStickyFloats ( )
{
2013-12-07 00:53:06 +00:00
_floatSet . Clear ( ) ;
2013-10-25 00:59:34 +00:00
}
2013-12-07 00:53:06 +00:00
public bool this [ string button ]
{
2013-10-25 00:59:34 +00:00
get
{
2013-12-07 00:53:06 +00:00
var source = Source [ button ] ;
2013-10-25 00:59:34 +00:00
source ^ = stickySet . Contains ( button ) ;
return source ;
}
2013-12-07 00:53:06 +00:00
set
{
throw new InvalidOperationException ( ) ;
}
2013-10-25 00:59:34 +00:00
}
2014-04-28 00:39:40 +00:00
/// <summary>
/// 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
/// </summary>
public bool StickyIsInEffect ( string button )
{
if ( IsSticky ( button ) )
{
return ! Source . IsPressed ( button ) ;
}
return false ;
}
2013-10-25 00:59:34 +00:00
public void SetSticky ( string button , bool isSticky )
{
2013-12-07 00:53:06 +00:00
if ( isSticky )
{
2013-10-25 00:59:34 +00:00
stickySet . Add ( button ) ;
2013-12-07 00:53:06 +00:00
}
else
{
stickySet . Remove ( button ) ;
}
2013-10-25 00:59:34 +00:00
}
2014-03-27 01:21:05 +00:00
public void Unset ( string button )
{
stickySet . Remove ( button ) ;
2014-06-29 21:16:33 +00:00
_floatSet . Remove ( button ) ;
2014-03-27 01:21:05 +00:00
}
2013-10-25 00:59:34 +00:00
public bool IsSticky ( string button )
{
return stickySet . Contains ( button ) ;
}
public HashSet < string > CurrentStickies
{
get
{
return stickySet ;
}
}
public void ClearStickies ( )
{
stickySet . Clear ( ) ;
2014-07-26 15:31:36 +00:00
_floatSet . Clear ( ) ;
2013-10-25 00:59:34 +00:00
}
public void MassToggleStickyState ( List < string > buttons )
{
2013-12-07 00:53:06 +00:00
foreach ( var button in buttons . Where ( button = > ! _justPressed . Contains ( button ) ) )
2013-10-25 00:59:34 +00:00
{
2013-12-07 00:53:06 +00:00
if ( stickySet . Contains ( button ) )
2013-10-25 00:59:34 +00:00
{
2013-12-07 00:53:06 +00:00
stickySet . Remove ( button ) ;
}
else
{
stickySet . Add ( button ) ;
2013-10-25 00:59:34 +00:00
}
}
2013-12-07 00:53:06 +00:00
_justPressed = buttons ;
2013-10-25 00:59:34 +00:00
}
2013-12-07 00:53:06 +00:00
private List < string > _justPressed = new List < string > ( ) ;
2013-10-25 00:59:34 +00:00
}
2014-04-28 00:39:40 +00:00
public class AutoFireStickyXorAdapter : IController , ISticky
2013-10-25 00:59:34 +00:00
{
public int On { get ; set ; }
public int Off { get ; set ; }
public WorkingDictionary < string , int > buttonStarts = new WorkingDictionary < string , int > ( ) ;
2013-12-07 00:53:06 +00:00
private readonly HashSet < string > _stickySet = new HashSet < string > ( ) ;
2013-10-25 00:59:34 +00:00
2013-12-07 00:53:06 +00:00
public IController Source { get ; set ; }
2013-10-25 00:59:34 +00:00
public void SetOnOffPatternFromConfig ( )
{
On = Global . Config . AutofireOn < 1 ? 0 : Global . Config . AutofireOn ;
Off = Global . Config . AutofireOff < 1 ? 0 : Global . Config . AutofireOff ;
}
2013-12-07 00:53:06 +00:00
public AutoFireStickyXorAdapter ( )
2013-10-25 00:59:34 +00:00
{
2013-12-07 00:53:06 +00:00
// On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn;
// Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff;
2013-10-25 00:59:34 +00:00
On = 1 ;
Off = 1 ;
}
public bool IsPressed ( string button )
{
2014-04-28 00:39:40 +00:00
return this [ button ] ;
2013-10-25 00:59:34 +00:00
}
public bool this [ string button ]
{
get
{
2013-12-07 00:53:06 +00:00
var source = Source [ button ] ;
2013-10-25 00:59:34 +00:00
2013-12-07 00:53:06 +00:00
if ( _stickySet . Contains ( button ) )
{
var a = ( Global . Emulator . Frame - buttonStarts [ button ] ) % ( On + Off ) ;
2013-10-25 00:59:34 +00:00
if ( a < On )
{
2014-04-28 00:39:40 +00:00
return source ^ = true ;
2013-10-25 00:59:34 +00:00
}
else
{
2014-04-28 00:39:40 +00:00
return source ^ = false ;
2013-10-25 00:59:34 +00:00
}
}
2014-04-28 00:39:40 +00:00
2013-10-25 00:59:34 +00:00
return source ;
}
2013-12-07 00:53:06 +00:00
set
{
throw new InvalidOperationException ( ) ;
}
}
2013-10-25 00:59:34 +00:00
public ControllerDefinition Type { get { return Source . Type ; } set { throw new InvalidOperationException ( ) ; } }
2013-12-07 00:53:06 +00:00
public bool Locked { get ; set ; } // Pretty much a hack,
2013-10-25 00:59:34 +00:00
// dumb passthrough for floats, because autofire doesn't care about them
2013-12-07 00:53:06 +00:00
public float GetFloat ( string name )
{
return Source . GetFloat ( name ) ;
}
2013-10-25 00:59:34 +00:00
public void SetSticky ( string button , bool isSticky )
{
if ( isSticky )
2013-12-07 00:53:06 +00:00
{
this . _stickySet . Add ( button ) ;
}
else
{
this . _stickySet . Remove ( button ) ;
}
2013-10-25 00:59:34 +00:00
}
public bool IsSticky ( string button )
{
2013-12-07 00:53:06 +00:00
return this . _stickySet . Contains ( button ) ;
2013-10-25 00:59:34 +00:00
}
public HashSet < string > CurrentStickies
{
get
{
2013-12-07 00:53:06 +00:00
return this . _stickySet ;
2013-10-25 00:59:34 +00:00
}
}
public void ClearStickies ( )
{
2013-12-07 00:53:06 +00:00
this . _stickySet . Clear ( ) ;
2013-10-25 00:59:34 +00:00
}
public void MassToggleStickyState ( List < string > buttons )
{
2013-12-07 00:53:06 +00:00
foreach ( var button in buttons . Where ( button = > ! _justPressed . Contains ( button ) ) )
2013-10-25 00:59:34 +00:00
{
2013-12-07 00:53:06 +00:00
if ( _stickySet . Contains ( button ) )
2013-10-25 00:59:34 +00:00
{
2013-12-07 00:53:06 +00:00
_stickySet . Remove ( button ) ;
}
else
{
_stickySet . Add ( button ) ;
2013-10-25 00:59:34 +00:00
}
}
2013-12-07 00:53:06 +00:00
_justPressed = buttons ;
2013-10-25 00:59:34 +00:00
}
2014-04-28 00:39:40 +00:00
/// <summary>
/// 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
/// </summary>
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
}
2013-12-07 00:53:06 +00:00
private List < string > _justPressed = new List < string > ( ) ;
2013-10-25 00:59:34 +00:00
}
/// <summary>
2013-12-07 00:53:06 +00:00
/// Just copies source to sink, or returns whatever a NullController would if it is disconnected. useful for immovable hardpoints.
2013-10-25 00:59:34 +00:00
/// </summary>
public class CopyControllerAdapter : IController
{
2013-12-07 00:53:06 +00:00
public IController Source { get ; set ; }
2013-10-25 00:59:34 +00:00
private readonly NullController _null = new NullController ( ) ;
2013-12-07 00:53:06 +00:00
private IController Curr
2013-10-25 00:59:34 +00:00
{
get
{
2013-12-07 00:53:06 +00:00
if ( Source = = null )
{
return _null ;
}
else
{
return Source ;
}
2013-10-25 00:59:34 +00:00
}
}
2013-12-07 00:53:06 +00:00
public ControllerDefinition Type
{
get { return Curr . Type ; }
}
2013-10-25 00:59:34 +00:00
2013-12-07 00:53:06 +00:00
public bool this [ string button ]
{
get { return Curr [ button ] ; }
}
public bool IsPressed ( string button )
{
return Curr . IsPressed ( button ) ;
}
public float GetFloat ( string name )
2013-10-25 00:59:34 +00:00
{
2013-12-07 00:53:06 +00:00
return Curr . GetFloat ( name ) ;
2013-10-25 00:59:34 +00:00
}
2013-12-07 00:53:06 +00:00
}
2013-10-25 00:59:34 +00:00
2014-03-29 21:12:04 +00:00
/// <summary>
/// Used to pass into an Override method to manage the logic overriding input
/// This only works with bool buttons!
/// </summary>
public class OverrideAdaptor : IController
{
private readonly Dictionary < string , bool > _overrides = new Dictionary < string , bool > ( ) ;
2014-05-21 03:25:41 +00:00
private readonly Dictionary < string , float > _floatOverrides = new Dictionary < string , float > ( ) ;
2014-03-29 21:12:04 +00:00
private readonly List < string > _inverses = new List < string > ( ) ;
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 Type { get ; set ; }
public IEnumerable < string > Overrides
{
get
{
foreach ( var kvp in _overrides )
{
yield return kvp . Key ;
}
}
}
2014-05-21 03:25:41 +00:00
public IEnumerable < string > FloatOverrides
{
get
{
foreach ( var kvp in _floatOverrides )
{
yield return kvp . Key ;
}
}
}
2014-03-29 21:12:04 +00:00
public IEnumerable < string > InversedButtons
{
get
{
foreach ( var name in _inverses )
{
yield return name ;
}
}
}
2014-05-21 03:25:41 +00:00
public void SetFloat ( string name , float value )
{
if ( _floatOverrides . ContainsKey ( name ) )
{
_floatOverrides [ name ] = value ;
}
else
{
_floatOverrides . Add ( name , value ) ;
}
}
2014-03-29 21:12:04 +00:00
public float GetFloat ( string name )
{
2014-05-21 03:25:41 +00:00
if ( _floatOverrides . ContainsKey ( name ) )
{
return _floatOverrides [ name ] ;
}
2014-03-29 21:12:04 +00:00
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 ( ) ;
2014-05-21 03:25:41 +00:00
_floatOverrides . Clear ( ) ;
2014-03-29 21:12:04 +00:00
_inverses . Clear ( ) ;
}
}
2013-10-25 00:59:34 +00:00
}