Files
Lithium/Lithium/helper.cs
T
Trenten Pettry 7d6bdc592f ## 🚀 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.
2026-06-15 03:55:58 -04:00

181 lines
5.9 KiB
C#

using System;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
namespace Lua
{
static class Helper
{
/// <summary>
// AA0030
/// </summary>
public static string[] ReadLinesSafe(string path)
{
try
{
if (!File.Exists(path)) return Array.Empty<string>();
return File.ReadAllLines(path);
}
catch
{
// AA0031
return Array.Empty<string>();
}
}
/// <summary>
// AA0032
/// </summary>
public static bool ParseBoolFromAssignment(string line, string key, out bool result)
{
result = false;
var idx = line.IndexOf('=');
if (idx < 0) return false;
var lhs = line.Substring(0, idx).Trim();
if (!string.Equals(lhs, key, StringComparison.OrdinalIgnoreCase)) return false;
var rhs = line.Substring(idx + 1).Trim();
if (rhs.Equals("true", StringComparison.OrdinalIgnoreCase))
{
result = true;
return true;
}
if (rhs.Equals("false", StringComparison.OrdinalIgnoreCase))
{
result = false;
return true;
}
// AA0033
return bool.TryParse(rhs, out result);
}
// AA0034
public static bool ParseIntFromAssignment(string line, string key, out int result)
{
result = 0;
var idx = line.IndexOf('=');
if (idx < 0) return false;
var lhs = line.Substring(0, idx).Trim();
if (!string.Equals(lhs, key, StringComparison.OrdinalIgnoreCase)) return false;
var rhs = line.Substring(idx + 1).Trim();
return int.TryParse(rhs, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
}
// AA0035
public static bool ParseStringFromAssignment(string line, string key, out string result)
{
result = null;
var idx = line.IndexOf('=');
if (idx < 0) return false;
var lhs = line.Substring(0, idx).Trim();
if (!string.Equals(lhs, key, StringComparison.OrdinalIgnoreCase)) return false;
result = line.Substring(idx + 1).Trim();
// AA0036 - Fixed: Check length before substring to avoid negative index
if (result.Length >= 2 && ((result.StartsWith("\"") && result.EndsWith("\"")) ||
(result.StartsWith("'") && result.EndsWith("'"))))
{
result = result.Substring(1, result.Length - 2);
}
return true;
}
// AA0037
public static bool ParseConsoleColorFromAssignment(string line, string key, out ConsoleColor color)
{
color = ConsoleColor.White;
if (!ParseStringFromAssignment(line, key, out var s)) return false;
return Enum.TryParse(s, true, out color);
}
/// <summary>
// AA0038
/// </summary>
public static bool TryParseRecoilLine(string line, out string name, out double recoilX, out double recoilY)
{
name = null;
recoilX = 0;
recoilY = 0;
// AA0039
var pattern = @"\[\s*[""'](.+?)[""']\s*\]\s*=\s*\{\s*recoilX\s*=\s*([-\d\.eE]+)\s*,\s*recoilY\s*=\s*([-\d\.eE]+)\s*\}";
var match = Regex.Match(line, pattern, RegexOptions.IgnoreCase);
if (!match.Success) return false;
name = match.Groups[1].Value.Trim();
return double.TryParse(match.Groups[2].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out recoilX) &&
double.TryParse(match.Groups[3].Value, NumberStyles.Float, CultureInfo.InvariantCulture, out recoilY);
}
/// <summary>
// AA0040
/// </summary>
public static int SafeClampIndex(int idx, int size)
{
if (size <= 0) return 0;
if (idx < 0) return 0;
if (idx >= size) return size - 1;
return idx;
}
public static bool ParseQuickSwap(string line, out int[] keys, out string targetOperator)
{
keys = null;
targetOperator = null;
var idx = line.IndexOf('=');
if (idx < 0) return false;
var lhs = line.Substring(0, idx).Trim();
if (!string.Equals(lhs, "quickSwap", StringComparison.OrdinalIgnoreCase)) return false;
var rhs = line.Substring(idx + 1).Trim();
var parts = rhs.Split(new[] { "=>" }, StringSplitOptions.None);
if (parts.Length != 2) return false;
var keyPart = parts[0].Trim();
var opPart = parts[1].Trim();
if (opPart.Length >= 2 && ((opPart.StartsWith("\"") && opPart.EndsWith("\"")) ||
(opPart.StartsWith("'") && opPart.EndsWith("'"))))
{
opPart = opPart.Substring(1, opPart.Length - 2);
}
targetOperator = opPart;
var keyStrings = keyPart.Split('+');
var keyList = new System.Collections.Generic.List<int>();
foreach (var kStr in keyStrings)
{
var trimmed = kStr.Trim();
int vk = 0;
if (trimmed.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
try { vk = Convert.ToInt32(trimmed, 16); } catch { return false; }
}
else if (int.TryParse(trimmed, out int val))
{
vk = val;
}
else
{
return false;
}
keyList.Add(vk);
}
if (keyList.Count == 0) return false;
keys = keyList.ToArray();
return true;
}
}
}