5f993f98b8
Increase the hotkey and recoil loop sleep intervals to reduce CPU churn and smooth input handling. The hotkey loop now waits 15ms instead of 8ms, and the recoil loop waits 10ms instead of 2ms, which helps reduce jitter while keeping responsiveness stable. This also updates the packaged release artifact by replacing the older archive with Lithium-0.01.7z.
136 lines
4.0 KiB
C#
136 lines
4.0 KiB
C#
using Lithium;
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
|
|
namespace Lua
|
|
{
|
|
internal static class RecoilEngine
|
|
{
|
|
private const int VK_LBUTTON = 0x01;
|
|
private const int VK_RBUTTON = 0x02;
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern short GetAsyncKeyState(int vKey);
|
|
|
|
private static readonly Random _rng = new Random();
|
|
private static bool _wasBothDown = false;
|
|
private static double _randomOffsetX = 0;
|
|
private static double _randomOffsetY = 0;
|
|
private static double _remainderX, _remainderY;
|
|
|
|
public static bool CustomModeActive = false;
|
|
public static double CustomRecoilX = 0;
|
|
public static double CustomRecoilY = 0;
|
|
|
|
private static bool IsDown(int key)
|
|
{
|
|
return (GetAsyncKeyState(key) & 0x8000) != 0;
|
|
}
|
|
|
|
private static CancellationTokenSource _cancellationSource;
|
|
|
|
public static void Start(MainWindow window, CancellationToken cancellationToken = default)
|
|
{
|
|
_cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
try
|
|
{
|
|
while (!_cancellationSource.Token.IsCancellationRequested)
|
|
{
|
|
bool leftDown = IsDown(VK_LBUTTON);
|
|
bool rightDown = IsDown(VK_RBUTTON);
|
|
bool bothDown = leftDown && rightDown;
|
|
|
|
// Detect new activation (transition from not both down to both down)
|
|
if (bothDown && !_wasBothDown)
|
|
{
|
|
OnSetActivated();
|
|
}
|
|
_wasBothDown = bothDown;
|
|
|
|
if (window.IsRecoilEnabled && bothDown)
|
|
{
|
|
HandleRecoilTick();
|
|
}
|
|
else
|
|
{
|
|
_remainderX = _remainderY = 0;
|
|
}
|
|
|
|
Thread.Sleep(10);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"RecoilEngine error: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private static void OnSetActivated()
|
|
{
|
|
if (!ConfigLoader.randomOffsetEnabled) return;
|
|
|
|
double range = ConfigLoader.randomOffsetMax - ConfigLoader.randomOffsetMin;
|
|
if (range <= 0) return;
|
|
|
|
_randomOffsetX = ConfigLoader.randomOffsetMin + _rng.NextDouble() * range;
|
|
_randomOffsetY = ConfigLoader.randomOffsetMin + _rng.NextDouble() * range;
|
|
}
|
|
|
|
private static void HandleRecoilTick()
|
|
{
|
|
try
|
|
{
|
|
if (CustomModeActive)
|
|
{
|
|
ApplyRecoil((CustomRecoilX, CustomRecoilY));
|
|
return;
|
|
}
|
|
|
|
string setName = OperatorSets.CurrentSet;
|
|
|
|
if (!OperatorSets.mergedSets.TryGetValue(setName, out var recoil)) return;
|
|
|
|
ApplyRecoil(recoil);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"HandleRecoilTick error: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private static void ApplyRecoil((double recoilX, double recoilY) recoil)
|
|
{
|
|
double moveX = recoil.recoilX + _randomOffsetX;
|
|
double moveY = recoil.recoilY + _randomOffsetY;
|
|
|
|
if (ConfigLoader.debitFlag1)
|
|
{
|
|
moveX *= 1.282;
|
|
moveY *= 1.351;
|
|
}
|
|
|
|
moveX += _remainderX;
|
|
moveY += _remainderY;
|
|
|
|
int ix = (int)Math.Round(moveX);
|
|
int iy = (int)Math.Round(moveY);
|
|
|
|
_remainderX = moveX - ix;
|
|
_remainderY = moveY - iy;
|
|
|
|
Utils.MoveMouse(ix, iy);
|
|
}
|
|
|
|
public static void Stop()
|
|
{
|
|
try
|
|
{
|
|
_cancellationSource?.Cancel();
|
|
_cancellationSource?.Dispose();
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
}
|