BizHawk/BizHawk.Client.EmuHawk/tools/VirtualPads/controls/VirtualPadAnalogStick.cs

270 lines
6.8 KiB
C#

using System;
using System.Drawing;
using System.Windows.Forms;
using BizHawk.Emulation.Common;
using BizHawk.Client.Common;
using System.Windows;
using BizHawk.Common.NumberExtensions;
namespace BizHawk.Client.EmuHawk
{
public partial class VirtualPadAnalogStick : UserControl, IVirtualPadControl
{
#region Fields
private bool _programmaticallyUpdatingNumerics;
private bool _readonly;
private int rangeAverageX; //for coordinate transformation when non orthogonal (like PSX for example)
private int rangeAverageY;
private EventHandler manualXYValueChangedEventHandler;
private EventHandler polarNumericChangedEventHandler;
#endregion
public VirtualPadAnalogStick()
{
InitializeComponent();
AnalogStick.ClearCallback = ClearCallback;
manualXYValueChangedEventHandler = new EventHandler(ManualXY_ValueChanged);
polarNumericChangedEventHandler = new EventHandler(PolarNumeric_Changed);
ManualX.ValueChanged += manualXYValueChangedEventHandler;
ManualY.ValueChanged += manualXYValueChangedEventHandler;
manualR.ValueChanged += polarNumericChangedEventHandler;
manualTheta.ValueChanged += polarNumericChangedEventHandler;
}
public float[] RangeX = new float[] { -128f, 0.0f, 127f };
public float[] RangeY = new float[] { -128f, 0.0f, 127f };
private void VirtualPadAnalogStick_Load(object sender, EventArgs e)
{
AnalogStick.Name = Name;
AnalogStick.XName = Name;
AnalogStick.YName = Name.Replace("X", "Y"); // TODO: allow schema to dictate this but this is a convenient default
AnalogStick.SetRangeX(RangeX);
AnalogStick.SetRangeY(RangeY);
ManualX.Minimum = (decimal)RangeX[0];
ManualX.Maximum = (decimal)RangeX[2];
ManualY.Minimum = (decimal)RangeX[0];
ManualY.Maximum = (decimal)RangeX[2];
MaxXNumeric.Minimum = 1;
MaxXNumeric.Maximum = 100;
MaxXNumeric.Value = 100;
MaxYNumeric.Minimum = 1;
MaxYNumeric.Maximum = 100;
MaxYNumeric.Value = 100; // Note: these trigger change events that change the analog stick too
rangeAverageX = (int)((RangeX[0] + RangeX[2]) / 2);
rangeAverageY = (int)((RangeY[0] + RangeY[2]) / 2);
}
#region IVirtualPadControl Implementation
public void UpdateValues()
{
// Nothing to do
// This tool already draws as necessary
}
public void Set(IController controller)
{
AnalogStick.Set(controller);
SetNumericsFromAnalog();
}
public void ClearCallback()
{
ManualX.Value = 0;
ManualY.Value = 0;
manualR.Value = 0;
manualTheta.Value = 0;
//see HOOMOO
Global.AutofireStickyXORAdapter.SetSticky(AnalogStick.XName, false);
Global.StickyXORAdapter.Unset(AnalogStick.XName);
Global.AutofireStickyXORAdapter.SetSticky(AnalogStick.YName, false);
Global.StickyXORAdapter.Unset(AnalogStick.YName);
AnalogStick.HasValue = false;
}
public void Clear()
{
AnalogStick.Clear();
}
public bool ReadOnly
{
get
{
return _readonly;
}
set
{
if (_readonly != value)
{
XLabel.Enabled =
ManualX.Enabled =
YLabel.Enabled =
ManualY.Enabled =
MaxLabel.Enabled =
MaxXNumeric.Enabled =
MaxYNumeric.Enabled =
manualR.Enabled =
rLabel.Enabled =
manualTheta.Enabled =
thetaLabel.Enabled =
!value;
AnalogStick.ReadOnly =
_readonly =
value;
Refresh();
}
}
}
#endregion
public void Bump(int? x, int? y)
{
if (x.HasValue)
{
AnalogStick.HasValue = true;
AnalogStick.X += x.Value;
}
if (y.HasValue)
{
AnalogStick.HasValue = true;
AnalogStick.Y += y.Value;
}
SetNumericsFromAnalog();
}
public void SetPrevious(IController previous)
{
AnalogStick.SetPrevious(previous);
}
private void ManualXY_ValueChanged(object sender, EventArgs e)
{
SetAnalogControlFromNumerics();
}
private void MaxManualXY_ValueChanged(object sender, EventArgs e)
{
SetAnalogMaxFromNumerics();
}
private void PolarNumeric_Changed(object sender, EventArgs e)
{
ManualX.ValueChanged -= manualXYValueChangedEventHandler;
ManualY.ValueChanged -= manualXYValueChangedEventHandler;
ManualX.Value = Math.Ceiling(manualR.Value * (decimal)Math.Cos(Math.PI * (double)manualTheta.Value / 180)).Clamp(-127, 127) + rangeAverageX;
ManualY.Value = Math.Ceiling(manualR.Value * (decimal)Math.Sin(Math.PI * (double)manualTheta.Value / 180)).Clamp(-127, 127) + rangeAverageY;
AnalogStick.X = (int)ManualX.Value;
AnalogStick.Y = (int)ManualY.Value;
AnalogStick.HasValue = true;
AnalogStick.Refresh();
ManualX.ValueChanged += manualXYValueChangedEventHandler;
ManualY.ValueChanged += manualXYValueChangedEventHandler;
}
private void SetAnalogControlFromNumerics()
{
if (!_programmaticallyUpdatingNumerics)
{
AnalogStick.X = (int)ManualX.Value;
AnalogStick.Y = (int)ManualY.Value;
AnalogStick.HasValue = true;
AnalogStick.Refresh();
}
}
private void SetNumericsFromAnalog()
{
_programmaticallyUpdatingNumerics = true;
if (AnalogStick.HasValue)
{
// Setting .Value of a numeric causes a draw, so avoid it unless necessary
if (ManualX.Value != AnalogStick.X)
{
ManualX.Value = AnalogStick.X;
}
if (ManualY.Value != AnalogStick.Y)
{
ManualY.Value = AnalogStick.Y;
}
}
else
{
if (ManualX.Value != 0)
{
ManualX.Value = 0;
}
if (ManualY.Value != 0)
{
ManualY.Value = 0;
}
}
manualR.ValueChanged -= polarNumericChangedEventHandler;
manualTheta.ValueChanged -= polarNumericChangedEventHandler;
manualR.Value = (decimal)Math.Sqrt(Math.Pow(AnalogStick.X - rangeAverageX, 2) + Math.Pow(AnalogStick.Y - rangeAverageY, 2));
manualTheta.Value = (decimal)(Math.Atan2(AnalogStick.Y - rangeAverageY, AnalogStick.X - rangeAverageX) * (180 / Math.PI));
manualR.ValueChanged += polarNumericChangedEventHandler;
manualTheta.ValueChanged += polarNumericChangedEventHandler;
_programmaticallyUpdatingNumerics = false;
}
private void AnalogStick_MouseDown(object sender, MouseEventArgs e)
{
if (!ReadOnly)
{
_programmaticallyUpdatingNumerics = true;
SetNumericsFromAnalog();
_programmaticallyUpdatingNumerics = false;
}
}
private void AnalogStick_MouseMove(object sender, MouseEventArgs e)
{
if (!ReadOnly)
{
_programmaticallyUpdatingNumerics = true;
SetNumericsFromAnalog();
_programmaticallyUpdatingNumerics = false;
}
}
private void SetAnalogMaxFromNumerics()
{
if (!_programmaticallyUpdatingNumerics)
{
//blehh,... this damn feature
AnalogStick.SetUserRange((float)MaxXNumeric.Value, (float)MaxYNumeric.Value);
}
}
}
}