dota-wheel/DotaWheel/Wheel.cs

482 lines
19 KiB
C#
Raw Normal View History

2022-08-02 00:12:37 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DotaWheel
{
class Wheel
{
/// <summary>
/// Enumeration of all buttons on a Logitech G29
/// Integers represent the values usable with the LogitechGSDK to get
/// information about the button.
/// </summary>
public enum Button : int
{
X = 0,
SQUARE = 1,
CIRCLE = 2,
TRIANGLE = 3,
RIGHT_PADDLE = 4,
LEFT_PADDLE = 5,
R2 = 6,
L2 = 7,
SHARE = 8,
OPTION = 9,
R3 = 10,
L3 = 11,
PLUS = 19,
MINUS = 20,
ROT_RIGHT = 21,
ROT_LEFT = 22,
ROT_CENTER = 23,
PS = 24,
}
/// <summary>
/// Different directions of DPAD input.
/// </summary>
public enum DpadButton
{
UP,
DOWN,
LEFT,
RIGHT,
}
/// <summary>
/// Different scalar values on the Logitech G29
/// </summary>
public enum ScalarInput
{
WHEEL,
ACCELERATOR,
BRAKE,
CLUTCH,
}
/// <summary>
/// The current state of the buttons. true=down, false=up.
/// </summary>
public Dictionary<Button, bool> Buttons { get; private set; }
/// <summary>
/// Buttons that were pressed or released since the last update. true=down, false=up.
/// If a button is in this dictionary, it means that its state changed since the last update.
/// </summary>
public Dictionary<Button, bool> ButtonsChanged { get; private set; }
/// <summary>
/// The current state of the DPAD represented as DpadButton s. true=down, false=up.
/// </summary>
public Dictionary<DpadButton, bool> DpadButtons { get; private set; }
/// <summary>
/// DPAD buttons that were pressed or released since the last update. true=down, false=up.
/// If a DpadButton is in this dictionary, it means that its state changed since the last update.
/// </summary>
public Dictionary<DpadButton, bool> DpadButtonsChanged { get; private set; }
/// <summary>
/// The position of the dpad on the previous update.
/// </summary>
private uint lastDpadPosition = uint.MaxValue;
/// <summary>
/// The position of the wheel on the previous update.
/// </summary>
private uint lastWheelPosition = 0;
/// <summary>
/// The position of the accelerator on the previous update.
/// </summary>
private uint lastAcceleratorPosition = 0;
/// <summary>
/// The position of the brake on the previous update.
/// </summary>
private uint lastBrakePosition = 0;
/// <summary>
/// THe position of the clutch on the previous update.
/// </summary>
private uint lastClutchPosition = 0;
/// <summary>
/// The current position of the DPAD in Degrees * 100.
/// <list type="bullet">
/// <item>0=up</item>
/// <item>4500=up-right</item>
/// <item>9000=right</item>
/// <item>13500=down-right</item>
/// <item>18000=down</item>
/// <item>22500=down-left</item>
/// <item>27000=left</item>
/// <item>32500=up-left</item>
/// </list>
/// </summary>
public uint DpadPosition { get; private set; }
/// <summary>
/// The current position of the steering wheel.
/// <list type="bullet">
/// <item>0=left</item>
/// <item>32768=center</item>
/// <item>65535=right</item>
/// </list>
/// </summary>
public uint WheelPosition { get; private set; }
/// <summary>
/// The current position of the accelerator.
/// <list type="bullet">
/// <item>0=depressed</item>
/// <item>65535=compressed</item>
/// </list>
/// </summary>
public uint AcceleratorPosition { get; private set; }
/// <summary>
/// The current position of the brake.
/// <list type="bullet">
/// <item>0=depressed</item>
/// <item>65535=compressed</item>
/// </list>
/// </summary>
public uint BrakePosition { get; private set; }
/// <summary>
/// The current position of the clutch.
/// <list type="bullet">
/// <item>0=depressed</item>
/// <item>65535=compressed</item>
/// </list>
/// </summary>
public uint ClutchPosition { get; private set; }
public Wheel()
{
Buttons = new Dictionary<Button, bool>();
ButtonsChanged = new Dictionary<Button, bool>();
DpadButtons = new Dictionary<DpadButton, bool>();
DpadButtonsChanged = new Dictionary<DpadButton, bool>();
DpadPosition = 0;
WheelPosition = 0;
AcceleratorPosition = 0;
BrakePosition = 0;
ClutchPosition = 0;
// Initialize the buttons
foreach (Button button in Enum.GetValues(typeof(Button)))
{
Buttons[button] = false;
}
foreach (DpadButton dpadButton in Enum.GetValues(typeof(DpadButton)))
{
DpadButtons[dpadButton] = false;
}
}
/// <summary>
/// Calculates whether a scalar input was pressed.
/// </summary>
/// <param name="lp">The previous position of the scalar input</param>
/// <param name="cp">The current position of the scalar input</param>
/// <param name="threshold">The threshold for the scalar input being down or up</param>
/// <param name="positiveDown">
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
/// </param>
/// <returns>Whether the scalar input was pressed.</returns>
private static bool PseudoScalarPressed(uint lp, uint cp, uint threshold, bool positiveDown = false)
{
if (positiveDown)
{
return cp >= threshold && lp < threshold;
}
else
{
return cp <= threshold && lp > threshold;
}
}
/// <summary>
/// Calculates whether a scalar input was released.
/// </summary>
/// <param name="lp">The previous position of the scalar input</param>
/// <param name="cp">The current position of the scalar input</param>
/// <param name="threshold">The threshold for the scalar input being down or up</param>
/// <param name="positiveDown">
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
/// </param>
/// <returns>Whether the scalar input was released.</returns>
private static bool PseudoScalarReleased(uint lp, uint cp, uint threshold, bool positiveDown = false)
{
if (positiveDown)
{
return cp < threshold && lp >= threshold;
}
else
{
return cp > threshold && lp <= threshold;
}
}
/// <summary>
/// Gets the DpadButton representation of the uint dpadPosition.
/// </summary>
/// <param name="dpadPosition">The position of the DPAD</param>
/// <returns>A set of DpadButtons that represents the buttons pressed by the dpadPosition</returns>
private static SortedSet<DpadButton> GetButtonsDown(uint dpadPosition)
{
SortedSet<DpadButton> ret = new SortedSet<DpadButton>();
if (dpadPosition < 9000 || (dpadPosition > 27000 && dpadPosition < 36000))
{
ret.Add(DpadButton.UP);
}
if (dpadPosition > 9000 && dpadPosition < 27000)
{
ret.Add(DpadButton.DOWN);
}
if (dpadPosition < 18000 && dpadPosition > 0)
{
ret.Add(DpadButton.RIGHT);
}
if (dpadPosition > 18000 && dpadPosition < 36000)
{
ret.Add(DpadButton.LEFT);
}
return ret;
}
/// <summary>
/// Gets the set of DpadButtons not in s
/// </summary>
/// <param name="s">A set of DpadButtons</param>
/// <returns>The set of DpadButtons not in the current set of DpadButtons</returns>
private static SortedSet<DpadButton> GetButtonsNotInSet(SortedSet<DpadButton> s)
{
SortedSet<DpadButton> ret = new SortedSet<DpadButton>();
foreach (DpadButton dpadButton in Enum.GetValues(typeof(DpadButton)))
{
ret.Add(dpadButton);
}
foreach (DpadButton dpadButton in s)
{
ret.Remove(dpadButton);
}
return ret;
}
/// <summary>
/// Returns whether a button was pressed in the last update.
/// </summary>
/// <param name="button">The button to check</param>
/// <returns>Whether the button was pressed in the last update</returns>
public bool ButtonPressed(Button button)
{
if (ButtonsChanged.TryGetValue(button, out bool pressed))
{
return pressed;
}
return false;
}
/// <summary>
/// Returns whether a Button was released in the last update.
/// </summary>
/// <param name="button">The Button to check</param>
/// <returns>Whether the button was pressed in the last update</returns>
public bool ButtonReleased(Button button)
{
if (ButtonsChanged.TryGetValue(button, out bool pressed))
{
return !pressed;
}
return false;
}
/// <summary>
/// Returns whether a DpadButton was pressed in the last update.
/// </summary>
/// <param name="dpadButton">The DpadButton to check</param>
/// <returns>Whether the DpadButton was pressed in the last update</returns>
public bool DpadButtonPressed(DpadButton dpadButton)
{
if (DpadButtonsChanged.TryGetValue(dpadButton, out bool pressed))
{
return pressed;
}
return false;
}
/// <summary>
/// Returns whether a DpadButton was released in the last update.
/// </summary>
/// <param name="dpadButton">The DpadButton to check</param>
/// <returns>Whether the DpadButton was released in the last update</returns>
public bool DpadButtonReleased(DpadButton dpadButton)
{
if (DpadButtonsChanged.TryGetValue(dpadButton, out bool pressed))
{
return !pressed;
}
return false;
}
/// <summary>
/// Returns whether the specified ScalarInput was pressed to a certain threshold in the last update.
/// </summary>
/// <param name="scalarInput">The ScalarInput to check</param>
/// <param name="threshold">The threshold for the ScalarInput to be considered pressed</param>
/// <param name="positiveDown">
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
/// </param>
/// <returns>Whether the ScalarInput was pressed to the threshold in the last update</returns>
public bool ScalarInputPressed(ScalarInput scalarInput, uint threshold, bool positiveDown = false)
{
switch (scalarInput)
{
case ScalarInput.WHEEL:
return PseudoScalarPressed(lastWheelPosition, WheelPosition, threshold, positiveDown);
case ScalarInput.ACCELERATOR:
return PseudoScalarPressed(lastAcceleratorPosition, AcceleratorPosition, threshold, positiveDown);
case ScalarInput.BRAKE:
return PseudoScalarPressed(lastBrakePosition, BrakePosition, threshold, positiveDown);
case ScalarInput.CLUTCH:
return PseudoScalarPressed(lastClutchPosition, ClutchPosition, threshold, positiveDown);
default:
throw new NotImplementedException("ScalarInput " + scalarInput.ToString() + " not implemented.");
}
}
/// <summary>
/// Returns whether the specified ScalarInput was released to a certain threshold in the last update.
/// </summary>
/// <param name="scalarInput">The ScalarInput to check</param>
/// <param name="threshold">The threshold for the ScalarInput to be considered released</param>
/// <param name="positiveDown">
/// Whether a higher number corrosponds to a pressed input. true=(higher number=pressed), false=(lower number=pressed)
/// </param>
/// <returns>Whether the ScalarInput was released from the threshold in the last update</returns>
public bool ScalarInputReleased(ScalarInput scalarInput, uint threshold, bool positiveDown = false)
{
switch (scalarInput)
{
case ScalarInput.WHEEL:
return PseudoScalarReleased(lastWheelPosition, WheelPosition, threshold, positiveDown);
case ScalarInput.ACCELERATOR:
return PseudoScalarReleased(lastAcceleratorPosition, AcceleratorPosition, threshold, positiveDown);
case ScalarInput.BRAKE:
return PseudoScalarReleased(lastBrakePosition, BrakePosition, threshold, positiveDown);
case ScalarInput.CLUTCH:
return PseudoScalarReleased(lastClutchPosition, ClutchPosition, threshold, positiveDown);
default:
throw new NotImplementedException("ScalarInput " + scalarInput.ToString() + " not implemented.");
}
}
/// <summary>
/// Helper function to update the a button on the specified wheel.
/// </summary>
/// <param name="wheelIndex">The index of the wheel for the LogitechGSDK</param>
/// <param name="button">The button to update</param>
private void UpdateButton(int wheelIndex, Button button)
{
if (LogitechGSDK.LogiButtonTriggered(wheelIndex, (int)button))
{
ButtonsChanged[button] = true;
Buttons[button] = true;
}
if (LogitechGSDK.LogiButtonReleased(wheelIndex, (int)button))
{
ButtonsChanged[button] = false;
Buttons[button] = false;
}
}
/// <summary>
/// Updates the DpadButtons and DpadButtonsChanged Dictionaries for the specified button
/// </summary>
/// <param name="dpadButton">The DpadButton to update</param>
/// <param name="cd">The set of currently down DpadButtons</param>
/// <param name="cu">The set of currently up DpadButtons</param>
/// <param name="ld">The set of previously down DpadButtons</param>
/// <param name="lu">The set of previously up DpadButtons</param>
private void UpdateDpadButton(
DpadButton dpadButton,
SortedSet<DpadButton> cd, SortedSet<DpadButton> cu,
SortedSet<DpadButton> ld, SortedSet<DpadButton> lu
)
{
if (cd.Contains(dpadButton) && !ld.Contains(dpadButton))
{
DpadButtonsChanged[dpadButton] = true;
DpadButtons[dpadButton] = true;
}
if (cu.Contains(dpadButton) && !lu.Contains(dpadButton))
{
DpadButtonsChanged[dpadButton] = false;
DpadButtons[dpadButton] = false;
}
}
/// <summary>
/// Updates the DpadButtons and DpadButtonsChanged Dictionaries
/// </summary>
/// <param name="wheel">The wheel to take the DPAD data from</param>
private void UpdateDpad(LogitechGSDK.DIJOYSTATE2ENGINES wheel)
{
DpadPosition = wheel.rgdwPOV[0];
SortedSet<DpadButton> currentButtonsDown = GetButtonsDown(DpadPosition);
SortedSet<DpadButton> currentButtonsUp = GetButtonsNotInSet(currentButtonsDown);
SortedSet<DpadButton> lastButtonsDown = GetButtonsDown(lastDpadPosition);
SortedSet<DpadButton> lastButtonsUp = GetButtonsNotInSet(lastButtonsDown);
foreach (DpadButton dpadButton in Enum.GetValues(typeof(DpadButton)))
{
UpdateDpadButton(dpadButton, currentButtonsDown, currentButtonsUp, lastButtonsDown, lastButtonsUp);
}
}
/// <summary>
/// Updates the class based on the current state of the wheelIndex.
/// </summary>
/// <see cref="LogitechGSDK"/>
/// <param name="wheelIndex">The index of the wheel to use to update</param>
public void Update(int wheelIndex)
{
// Reset Changed Dictionaries
ButtonsChanged = new Dictionary<Button, bool>();
DpadButtonsChanged = new Dictionary<DpadButton, bool>();
// Update Last Values
lastDpadPosition = DpadPosition;
lastWheelPosition = WheelPosition;
lastAcceleratorPosition = AcceleratorPosition;
lastBrakePosition = BrakePosition;
lastClutchPosition = ClutchPosition;
// Load the wheel from the LogitechGSDK
LogitechGSDK.DIJOYSTATE2ENGINES wheel = LogitechGSDK.LogiGetStateCSharp(wheelIndex);
// Update Buttons
foreach (Button button in Enum.GetValues(typeof(Button)))
{
UpdateButton(wheelIndex, button);
}
// Update the D-pad
UpdateDpad(wheel);
// Update Wheel
WheelPosition = (uint)(wheel.lX + 32768);
// Update Accelerator
AcceleratorPosition = (uint)(wheel.lY + 32768);
// Update Brake
BrakePosition = (uint)(wheel.lRz + 32768);
// Update Clutch
ClutchPosition = (uint)(wheel.rglSlider[0] + 32768);
}
}
}