## 🚀 Added (New Features)

• Quick Swap Feature:
      • Users can now bind keyboard keys (both single keys and multi-key combinations, e.g.,  Alt+1 ) to switch to
      specific operator sets.
      • Auto-toggle capability: Pressing the hotkey switches to the target operator set (automatically changing side
      if the operator is on standard Attackers/Defenders side). Pressing it again toggles back to the baseline
      operator set.
      • Added specificity filtering: Resolves overlapping hotkeys (e.g., pressing  Alt+1  will only trigger the  Alt+1
      binding, successfully ignoring the individual  1  key binding).
      • Added a dedicated Quick Swap Bindings editor inside the Settings panel to configure, save, and reload these
      bindings visually.

  ## ⚙️ Updated (Refactoring & Architecture)

  • Graceful Thread Shutdown: Updated RecoilEngine.cs to run with a  CancellationToken  linked to the main window
  lifespan, matching HotkeyHandler.cs and preventing background thread leaks when exiting.
  • State Management: Updated  OperatorSets  methods to protect the quick swap transition state, ensuring manual
  adjustments (clicks or next/prev hotkeys) reset the quick-swap toggle baseline.
  • Config Serialization: Updated  MainWindow  configuration generator to save new quick-swap parameters cleanly back
  to  init.config .

  ## 🛠️ Fixed (Bugs resolved)

  • TabControl/Menu Text Colors: Fixed the styling on MainWindow.xaml tab headers. Unselected and hover states now
  render in clear light greys, and active tabs in solid white, resolving dark-on-dark invisible text bugs.
  • Dropdown Selection Visibility: Changed the text foreground color of  TextColorComboBox  in MainWindow.xaml from
  black to white to make choices readable in dark mode.
  • UI Console Text Color: Bound the console setting ( textColor ) to the actual WPF GUI log ( ConsoleOutput.
  Foreground ), making the chosen color reflect in the application window.
  ──────
  ### 📂 File-Level Changes Summary

  • helper.cs: Added helper.cs to safely parse combinations.
  • ConfigLoader.cs: Added ConfigLoader.cs class and loading logic.
  • OperatorSets.cs: Added OperatorSets.cs and state-tracking flags.
  • HotkeyHandler.cs: Registered hotkey monitoring loops for multi-key patterns.
  • RecoilEngine.cs: Integrated thread lifecycle cancellation.
  • MainWindow.xaml: Styled menus, fixed dropdown contrast, and added Quick Swap bindings textbox.
  • MainWindow.xaml.cs: Connected UI controls to backend quick-swap actions and applied dynamic log coloring.
This commit is contained in:
Trenten Pettry
2026-06-15 03:55:58 -04:00
parent d6d8a60b20
commit 7d6bdc592f
13 changed files with 646 additions and 106 deletions
+51 -22
View File
@@ -1,4 +1,4 @@
using Lithium;
using Lithium;
using System;
using System.Runtime.InteropServices;
using System.Threading;
@@ -25,36 +25,55 @@ namespace Lua
return (GetAsyncKeyState(key) & 0x8000) != 0;
}
public static void Start(MainWindow window)
{
while (true)
{
if (window.IsRecoilEnabled && IsDown(VK_LBUTTON) && IsDown(VK_RBUTTON))
{
HandleRecoilTick();
}
else
{
_remainderX = _remainderY = 0;
_jitterActive = false;
}
private static CancellationTokenSource _cancellationSource;
Thread.Sleep(2);
public static void Start(MainWindow window, CancellationToken cancellationToken = default)
{
_cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
try
{
while (!_cancellationSource.Token.IsCancellationRequested)
{
// FIX: Perform key checks once and reuse the results
bool leftDown = IsDown(VK_LBUTTON);
bool rightDown = IsDown(VK_RBUTTON);
if (window.IsRecoilEnabled && leftDown && rightDown)
{
HandleRecoilTick();
}
else
{
_remainderX = _remainderY = 0;
_jitterActive = false;
}
Thread.Sleep(2);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"RecoilEngine error: {ex.Message}");
}
}
private static void HandleRecoilTick()
{
if (!IsDown(VK_LBUTTON)) return;
try
{
string setName = OperatorSets.CurrentSet;
string setName = OperatorSets.CurrentSet;
if (!OperatorSets.mergedSets.TryGetValue(setName, out var recoil)) return;
if (!OperatorSets.mergedSets.TryGetValue(setName, out var recoil)) return;
int now = Environment.TickCount;
int now = Environment.TickCount;
HandleJitter(now);
ApplyRecoil(recoil);
HandleJitter(now);
ApplyRecoil(recoil);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"HandleRecoilTick error: {ex.Message}");
}
}
private static void HandleJitter(int now)
@@ -103,5 +122,15 @@ namespace Lua
Utils.MoveMouse(ix, iy);
}
public static void Stop()
{
try
{
_cancellationSource?.Cancel();
_cancellationSource?.Dispose();
}
catch { }
}
}
}