8 Commits

Author SHA1 Message Date
TJhost 19556f2d26 Relocate app data and package build output
Adds a new `AppPaths` helper and moves runtime/user-generated files (`init.config`, `custom_set.txt`, cache, WebView2 data) into `%LocalAppData%\Lithium` with readable fallback to portable files. WebView2 initialization now sets a loader DLL path, uses an explicit user data folder, and surfaces runtime-missing errors.

The build/publish pipeline is updated to emit a clean `Build\` layout with dependencies under `lib\`, remove duplicate root DLL/docs, and align probing/runtime settings for net48 x64 output. Supporting updates include ignore rules for build/runtime artifacts, refreshed agent build/release docs, and endpoint URL/domain updates.
2026-09-13 19:33:09 -04:00
TJhost 5f993f98b8 Tweak input polling timings
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.
2026-09-13 18:27:58 -04:00
TJhost ae3b4164f3 🚀 Added (New Features)
• Random Offset feature: Applies random X/Y offset values on each set activation (both mouse buttons pressed)
• Each axis gets independent random values within configurable range (e.g., 0.003 and 0.008)
• New random values generated on every activation
• Toggle to enable/disable the feature

🛠️ Fixed (Bugs resolved)
• Replaced jitter system (burst/cooldown) with simpler random offset system

📂 File-Level Changes Summary
• ConfigLoader.cs: Added randomOffsetEnabled, randomOffsetMin, randomOffsetMax settings
• RecoilEngine.cs: Implemented random offset logic with activation detection
• MainWindow.xaml: Replaced Jitter Settings with Random Offset Settings UI
• MainWindow.xaml.cs: Updated LoadSettingsToUI, GenerateConfigContent, ResetSettings_Click
• helper.cs: Added ParseDoubleFromAssignment helper method
2026-09-03 16:00:27 -04:00
TJhost 86c231773d 📂 File-Level Changes Summary
• AGENTS.md: Added build & release workflow documentation
• session-ses_1101.md: Added session tracking file
2026-09-03 15:27:40 -04:00
Trenten Pettry b6037e0c91 🚀 Added (New Features)
• Custom Set Builder Testing Mode: The Custom Set Builder tab now functions as a live recoil testing tool. When toggled ON while the tab is active, the custom X/Y recoil values are applied instead of the selected operator's recoil, allowing real-time dial-in of recoil sets. Switching to any other tab automatically restores normal operator recoil.

📂 File-Level Changes Summary
• MainWindow.xaml: Named TabControl and added SelectionChanged handler for tab tracking.
• MainWindow.xaml.cs: Added UpdateCustomSetState() to sync custom recoil mode with RecoilEngine based on tab focus and toggle state.
• RecoilEngine.cs: Added CustomModeActive, CustomRecoilX, CustomRecoilY fields; HandleRecoilTick() checks custom mode first before falling back to mergedSets lookup.
2026-06-22 11:48:10 -04:00
Trenten Pettry 60b519335f 🛠️ Fixed (Bugs resolved)
- Quick Swap Side Restoration: Fixed a bug where quickswap would fail to switch sides when toggling back to the original operator. The issue occurred because the baseline set was captured after the side had already switched, so the revert logic couldn't determine it needed to switch sides again. Now the baseline is captured before any side change, ensuring correct toggle behavior.
📂 File-Level Changes Summary
- OperatorSets.cs: Captured preSwitchSet before the SwitchSide() call in QuickSwapTo() and used it when storing originalSetBeforeQuickSwap.
2026-06-22 11:33:26 -04:00
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
Trenten Pettry d6d8a60b20 gng 2026-04-28 15:29:42 -04:00
21 changed files with 4252 additions and 223 deletions
+14
View File
@@ -17,6 +17,9 @@
mono_crash.* mono_crash.*
# Build results # Build results
[Bb]in/
[Oo]bj/
[Bb]uild/
[Dd]ebug/ [Dd]ebug/
[Dd]ebugPublic/ [Dd]ebugPublic/
[Rr]elease/ [Rr]elease/
@@ -228,6 +231,17 @@ _pkginfo.txt
# but keep track of directories ending in .cache # but keep track of directories ending in .cache
!?*.[Cc]ache/ !?*.[Cc]ache/
# Runtime-generated application data
*.exe.WebView2/
# Local release archives
*.7z
*.zip
# Local Claude Code and VS Code settings
.claude/
.vscode/
# Others # Others
ClientBin/ ClientBin/
~$* ~$*
+87
View File
@@ -0,0 +1,87 @@
# Build & Release Workflow
## Prerequisites
- Git remote: `https://git.scpsmp.site/JJ/Lithium.git`
- Auth: OAuth token stored in Windows Credential Manager (via git)
- Visual Studio 2022 or newer with the .NET desktop development workload
- NuGet package restore enabled
- .NET Framework 4.8 Developer Pack for building
- Microsoft Edge WebView2 Runtime for the Website tab at runtime
## Build
```powershell
# Kill running process first
Stop-Process -Name "Lithium" -ErrorAction SilentlyContinue
# Locate MSBuild, restore packages, and compile the Release build
$msbuild = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | Select-Object -First 1
& $msbuild Lithium.sln /t:Restore,Rebuild /p:Configuration=Release /p:Platform="x64"
# Create the clean file-deployment layout under Build\.
& $msbuild Lithium\Lithium.csproj /t:Publish /p:Configuration=Release /p:Platform="x64"
# Keep Lithium.exe, Lithium.exe.config, and the complete lib directory together.
```
## Stage & Commit
```powershell
# Stage only intentional files (not build artifacts, .vs/, bin/, obj/)
git add Lithium/MainWindow.xaml Lithium/MainWindow.xaml.cs Lithium/RecoilEngine.cs
# Commit with changelog format
git commit -m "🚀 Added (New Features)
• Description of feature
🛠️ Fixed (Bugs resolved)
• Description of fix
📂 File-Level Changes Summary
• File.cs: Specific change description."
```
## Push & Release
```powershell
# Use naming scheme: 0.XX (e.g. 0.05, 0.06)
$version = "0.XX"
# Create and push tag
git tag $version
git push origin master
git push origin $version
# Retrieve OAuth token from git credential store
$cred = "protocol=https`nhost=git.scpsmp.site`n`n" | git credential fill
$token = ($cred -split "`n" | Where-Object { $_ -like "password=*" }) -replace "password=",""
# Archive the published x64 Release output; run from anywhere inside the repository
$repoRoot = (git rev-parse --show-toplevel).Trim()
$archivePath = Join-Path $repoRoot "Lithium-$version.7z"
$releaseOutput = Join-Path $repoRoot "Build\*"
$sevenZip = (Get-Command 7z.exe -ErrorAction Stop).Source
& $sevenZip a -mx=9 "$archivePath" "$releaseOutput"
# Create release via Gitea API
$body = @"
{"tag_name":"$version","target_commitish":"master","name":"$version @ Lithium","body":"🚀 Added (New Features)\\n• Description\\n\\n📂 File-Level Changes Summary\\n• File.cs: Change.","draft":false,"prerelease":false}
"@
[System.IO.File]::WriteAllText("$env:TEMP\release.json", $body)
$response = curl.exe -s -X POST "https://git.scpsmp.site/api/v1/repos/JJ/Lithium/releases" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d "@$env:TEMP\release.json"
$releaseId = ($response | ConvertFrom-Json).id
# Upload 7z asset
curl.exe -s -X POST "https://git.scpsmp.site/api/v1/repos/JJ/Lithium/releases/$releaseId/assets?name=Lithium-$version.7z" -H "Authorization: Bearer $token" -H "Content-Type: application/x-7z-compressed" --data-binary "$archivePath"
```
## Post-Release Cleanup
```powershell
# Delete the local archive after successful upload
Remove-Item -LiteralPath "$archivePath" -Force
Remove-Item -LiteralPath "$env:TEMP\release.json" -Force
```
## Notes
- Do NOT stage build artifacts (bin/, obj/, .vs/, .7z files)
- Tag naming scheme: `0.01`, `0.02`, `0.03`, etc. (not `v1.0.0`)
- Release naming scheme: `0.XX @ Lithium`
- Use PATCH to edit existing release tag via API if you mess up the tag name
- Add `7z.exe` to `PATH` before running the release archive command
BIN
View File
Binary file not shown.
+3
View File
@@ -0,0 +1,3 @@
{
"dotnet.preferCSharpExtension": true
}
+9 -6
View File
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<runtime> <startup useLegacyV2RuntimeActivationPolicy="true">
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
<probing privatePath="lib;dependencies;bin" /> </startup>
</assemblyBinding> <runtime>
</runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="lib" />
</assemblyBinding>
</runtime>
</configuration> </configuration>
+1 -1
View File
@@ -1,7 +1,7 @@
<Application x:Class="Lithium.App" <Application x:Class="Lithium.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"> StartupUri="../MainWindow.xaml">
<Application.Resources> <Application.Resources>
</Application.Resources> </Application.Resources>
</Application> </Application>
-1
View File
@@ -7,7 +7,6 @@
<package id="System.Memory" version="4.6.3" targetFramework="net48" /> <package id="System.Memory" version="4.6.3" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" /> <package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" /> <package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" />
<package id="System.Speech" version="10.0.0" targetFramework="net48" />
<package id="System.Text.Encodings.Web" version="10.0.0" targetFramework="net48" /> <package id="System.Text.Encodings.Web" version="10.0.0" targetFramework="net48" />
<package id="System.Text.Json" version="10.0.0" targetFramework="net48" /> <package id="System.Text.Json" version="10.0.0" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" /> <package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
+55
View File
@@ -0,0 +1,55 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Lua
{
internal static class AppPaths
{
private const string ApplicationDirectoryName = "Lithium";
public static string ExecutableDirectory => AppDomain.CurrentDomain.BaseDirectory;
public static string UserDataDirectory => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
ApplicationDirectoryName);
public static string WebViewUserDataDirectory => Path.Combine(UserDataDirectory, "WebView2");
public static string WebViewLoaderDirectory => Path.Combine(
ExecutableDirectory,
"lib",
"runtimes",
GetRuntimeIdentifier(),
"native");
private static string GetRuntimeIdentifier()
{
switch (RuntimeInformation.ProcessArchitecture)
{
case Architecture.X64:
return "win-x64";
case Architecture.Arm64:
return "win-arm64";
default:
return "win-x86";
}
}
public static string GetUserFilePath(string fileName)
{
Directory.CreateDirectory(UserDataDirectory);
return Path.Combine(UserDataDirectory, fileName);
}
public static string GetReadableFilePath(string fileName)
{
string userPath = Path.Combine(UserDataDirectory, fileName);
if (File.Exists(userPath))
return userPath;
string portablePath = Path.Combine(ExecutableDirectory, fileName);
return File.Exists(portablePath) ? portablePath : null;
}
}
}
+29 -16
View File
@@ -1,10 +1,19 @@
using System; using System;
using System.IO; using System.IO;
namespace Lua namespace Lua
{ {
public class QuickSwapBinding
{
public int[] Keys { get; set; }
public string TargetOperator { get; set; }
public bool IsHeld { get; set; }
}
internal static class ConfigLoader internal static class ConfigLoader
{ {
public static System.Collections.Generic.List<QuickSwapBinding> quickSwaps = new System.Collections.Generic.List<QuickSwapBinding>();
// AA0001 // AA0001
public static bool debitFlag1 = false; public static bool debitFlag1 = false;
// AA0002 // AA0002
@@ -33,11 +42,11 @@ namespace Lua
public static string userName = null; public static string userName = null;
// AA0013 // AA0013
public static bool jitterEnabled = false; public static bool randomOffsetEnabled = false;
// AA0014 // AA0014
public static int jitterBurstTime = 1800; public static double randomOffsetMin = 0.0001;
// AA0015 // AA0015
public static int jitterCooldown = 3000; public static double randomOffsetMax = 0.001;
// AA0016 // AA0016
public static ConsoleColor textColor = ConsoleColor.Magenta; public static ConsoleColor textColor = ConsoleColor.Magenta;
@@ -54,17 +63,19 @@ namespace Lua
favoriteAttackers = null; favoriteAttackers = null;
favoriteDefenders = null; favoriteDefenders = null;
userName = null; userName = null;
jitterEnabled = false; randomOffsetEnabled = false;
jitterBurstTime = 1800; randomOffsetMin = 0.0001;
jitterCooldown = 3000; randomOffsetMax = 0.001;
textColor = ConsoleColor.Magenta; textColor = ConsoleColor.Magenta;
quickSwaps.Clear();
SetConsoleColor(); SetConsoleColor();
// AA0019 // AA0019
if (!File.Exists("init.config")) return; string configPath = AppPaths.GetReadableFilePath("init.config");
if (configPath == null) return;
var lines = Helper.ReadLinesSafe("init.config"); var lines = Helper.ReadLinesSafe(configPath);
// AA0020 // AA0020
foreach (var raw in lines) foreach (var raw in lines)
@@ -98,16 +109,18 @@ namespace Lua
favoriteDefenders = s2; favoriteDefenders = s2;
else if (Helper.ParseStringFromAssignment(line, "userName", out var s3)) else if (Helper.ParseStringFromAssignment(line, "userName", out var s3))
userName = s3; userName = s3;
// AA0025 // AA0025 - Random Offset Settings
else if (Helper.ParseBoolFromAssignment(line, "jitterEnabled", out var jb)) else if (Helper.ParseBoolFromAssignment(line, "randomOffsetEnabled", out var roe))
jitterEnabled = jb; randomOffsetEnabled = roe;
else if (Helper.ParseIntFromAssignment(line, "jitterBurstTime", out var jbT)) else if (Helper.ParseDoubleFromAssignment(line, "randomOffsetMin", out var rom))
jitterBurstTime = jbT; randomOffsetMin = rom;
else if (Helper.ParseIntFromAssignment(line, "jitterCooldown", out var jbC)) else if (Helper.ParseDoubleFromAssignment(line, "randomOffsetMax", out var roM))
jitterCooldown = jbC; randomOffsetMax = roM;
// AA0026 // AA0026
else if (Helper.ParseConsoleColorFromAssignment(line, "textColor", out var col)) else if (Helper.ParseConsoleColorFromAssignment(line, "textColor", out var col))
textColor = col; textColor = col;
else if (Helper.ParseQuickSwap(line, out var qsKeys, out var qsOp))
quickSwaps.Add(new QuickSwapBinding { Keys = qsKeys, TargetOperator = qsOp });
} }
// AA0027 // AA0027
+111 -16
View File
@@ -1,4 +1,5 @@
using Lua; using Lua;
using System;
using System.Threading; using System.Threading;
namespace Lithium namespace Lithium
@@ -13,24 +14,49 @@ namespace Lithium
// AA0043 // AA0043
private static MainWindow _mainWindow; private static MainWindow _mainWindow;
// AA0044 // AA0044 - Added: Cancellation token for graceful shutdown
public static void Start(MainWindow window) private static CancellationTokenSource _cancellationSource;
// AA0045 - Updated: Accept cancellation token
public static void Start(MainWindow window, CancellationToken cancellationToken = default)
{ {
_mainWindow = window; _mainWindow = window;
_cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
// AA0045 try
while (true)
{ {
HandleToggleCapsLock(); while (!_cancellationSource.Token.IsCancellationRequested)
HandleNextSet(); {
HandlePrevSet(); try
HandleRefresh(); {
HandleSwitchSide(); HandleToggleCapsLock();
Thread.Sleep(8); HandleNextSet();
HandlePrevSet();
HandleRefresh();
HandleSwitchSide();
HandleQuickSwaps();
Thread.Sleep(15);
}
catch (Exception ex)
{
// Log error but continue running
System.Diagnostics.Debug.WriteLine($"HotkeyHandler error: {ex.Message}");
}
}
}
finally
{
_cancellationSource?.Dispose();
} }
} }
// AA0046 // AA0046 - Static method to stop the handler gracefully
public static void Stop()
{
_cancellationSource?.Cancel();
}
// AA0047
private static void HandleToggleCapsLock() private static void HandleToggleCapsLock()
{ {
bool capsDown = Utils.IsPressed(CAPSLOCK); bool capsDown = Utils.IsPressed(CAPSLOCK);
@@ -42,7 +68,7 @@ namespace Lithium
if (!capsDown) _capsHeld = false; if (!capsDown) _capsHeld = false;
} }
// AA0047 // AA0048
private static void HandleNextSet() private static void HandleNextSet()
{ {
if (Utils.IsPressed(ConfigLoader.nextSetKey) && !_nextSetHeld) if (Utils.IsPressed(ConfigLoader.nextSetKey) && !_nextSetHeld)
@@ -56,7 +82,7 @@ namespace Lithium
} }
} }
// AA0048 // AA0049
private static void HandlePrevSet() private static void HandlePrevSet()
{ {
if (Utils.IsPressed(ConfigLoader.prevSetKey) && !_prevSetHeld) if (Utils.IsPressed(ConfigLoader.prevSetKey) && !_prevSetHeld)
@@ -70,7 +96,7 @@ namespace Lithium
} }
} }
// AA0049 // AA0050
private static void HandleRefresh() private static void HandleRefresh()
{ {
if (Utils.IsPressed(ConfigLoader.refreshKey) && !_refreshHeld) if (Utils.IsPressed(ConfigLoader.refreshKey) && !_refreshHeld)
@@ -84,7 +110,7 @@ namespace Lithium
} }
} }
// AA0050 // AA0051
private static void HandleSwitchSide() private static void HandleSwitchSide()
{ {
if (Utils.IsPressed(ConfigLoader.switchSideKey) && !_switchSideHeld) if (Utils.IsPressed(ConfigLoader.switchSideKey) && !_switchSideHeld)
@@ -97,5 +123,74 @@ namespace Lithium
_switchSideHeld = false; _switchSideHeld = false;
} }
} }
private static void HandleQuickSwaps()
{
if (ConfigLoader.quickSwaps == null || ConfigLoader.quickSwaps.Count == 0) return;
// Find all bindings that are currently fully pressed
var activeBindings = new System.Collections.Generic.List<Lua.QuickSwapBinding>();
foreach (var qs in ConfigLoader.quickSwaps)
{
bool allPressed = true;
foreach (var k in qs.Keys)
{
if (!Lua.Utils.IsPressed(k))
{
allPressed = false;
break;
}
}
if (allPressed)
{
activeBindings.Add(qs);
}
}
// Filter out bindings that are subsets of other active bindings
var triggeredBindings = new System.Collections.Generic.List<Lua.QuickSwapBinding>();
foreach (var ab in activeBindings)
{
bool hasSuperset = false;
foreach (var other in activeBindings)
{
if (other != ab && other.Keys.Length > ab.Keys.Length)
{
bool isSubset = true;
foreach (var k in ab.Keys)
{
bool found = false;
foreach (var ok in other.Keys)
{
if (ok == k) { found = true; break; }
}
if (!found) { isSubset = false; break; }
}
if (isSubset) { hasSuperset = true; break; }
}
}
if (!hasSuperset)
{
triggeredBindings.Add(ab);
}
}
// Update held status and trigger actions
foreach (var qs in ConfigLoader.quickSwaps)
{
if (triggeredBindings.Contains(qs))
{
if (!qs.IsHeld)
{
qs.IsHeld = true;
_mainWindow.HandleQuickSwap(qs.TargetOperator);
}
}
else
{
qs.IsHeld = false;
}
}
}
} }
} }
+60 -11
View File
@@ -16,7 +16,7 @@
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<NuGetPackageImportStamp> <NuGetPackageImportStamp>
</NuGetPackageImportStamp> </NuGetPackageImportStamp>
<PublishUrl>publish\</PublishUrl> <PublishUrl>..\Build\</PublishUrl>
<Install>true</Install> <Install>true</Install>
<InstallFrom>Disk</InstallFrom> <InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled> <UpdateEnabled>false</UpdateEnabled>
@@ -62,7 +62,7 @@
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion> <LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath> <OutputPath>bin\x64\Release\</OutputPath>
@@ -72,7 +72,7 @@
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion> <LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
@@ -91,20 +91,49 @@
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath> <HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.IO, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.4.3.0\lib\net462\System.IO.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.IO.Pipelines, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.IO.Pipelines, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Pipelines.10.0.0\lib\net462\System.IO.Pipelines.dll</HintPath> <HintPath>..\packages\System.IO.Pipelines.10.0.0\lib\net462\System.IO.Pipelines.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath> <HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Net.Http, Version=4.1.1.3, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Numerics" /> <Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath> <HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Runtime, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath> <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Security.Cryptography.Algorithms, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net463\System.Security.Cryptography.Algorithms.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Encoding, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Primitives, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Speech" /> <Reference Include="System.Speech" />
<Reference Include="System.Text.Encodings.Web, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Text.Encodings.Web, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Encodings.Web.10.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath> <HintPath>..\packages\System.Text.Encodings.Web.10.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath>
@@ -120,7 +149,6 @@
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml"> <Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework> <RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference> </Reference>
@@ -133,6 +161,7 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Compile Include="AppPaths.cs" />
<Compile Include="HotkeyHandler.cs" /> <Compile Include="HotkeyHandler.cs" />
<Compile Include="RecoilEngine.cs" /> <Compile Include="RecoilEngine.cs" />
<Compile Include="Utils.cs" /> <Compile Include="Utils.cs" />
@@ -171,6 +200,7 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
<None Include="A1\packages.config" /> <None Include="A1\packages.config" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput> <LastGenOutput>Settings.Designer.cs</LastGenOutput>
@@ -202,16 +232,35 @@
<Error Condition="!Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets'))" /> <Error Condition="!Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets'))" />
</Target> </Target>
<Import Project="..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" /> <Import Project="..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" />
<ItemGroup>
<!-- Include all referenced DLLs -->
<ReferenceCopyLocalPaths Include="@(ReferencePath)" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Resource Include="A1\a23ae8c4c6863364165637fb70679eb3.ico" /> <Resource Include="A1\a23ae8c4c6863364165637fb70679eb3.ico" />
</ItemGroup> </ItemGroup>
<Target Name="CopyDllsToLib" AfterTargets="Build"> <Target Name="ClearGeneratedLib" BeforeTargets="CopyFilesToOutputDirectory">
<MakeDir Directories="$(OutputPath)lib" /> <RemoveDir Directories="$(TargetDir)lib" Condition="Exists('$(TargetDir)lib')" />
<Copy SourceFiles="@(ReferenceCopyLocalPaths)" DestinationFolder="$(OutputPath)lib" SkipUnchangedFiles="true" /> </Target>
<Target Name="OrganizeBuildOutput" AfterTargets="CopyFilesToOutputDirectory">
<ItemGroup>
<ManagedDependencies Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.dll'" />
<DependencyDocumentation Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.xml'" />
<WebViewNativeFiles Include="$(TargetDir)runtimes\**\*" />
</ItemGroup>
<MakeDir Directories="$(TargetDir)lib" />
<Copy SourceFiles="@(ManagedDependencies)" DestinationFolder="$(TargetDir)lib" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(WebViewNativeFiles)" DestinationFiles="@(WebViewNativeFiles->'$(TargetDir)lib\runtimes\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="true" Condition="'@(WebViewNativeFiles)' != ''" />
<Delete Files="@(ManagedDependencies->'$(TargetDir)%(Filename)%(Extension)');@(DependencyDocumentation->'$(TargetDir)%(Filename)%(Extension)')" />
<Delete Files="$(TargetDir)$(TargetName).pdb" Condition="'$(Configuration)' == 'Release' and Exists('$(TargetDir)$(TargetName).pdb')" />
<RemoveDir Directories="$(TargetDir)runtimes" Condition="Exists('$(TargetDir)runtimes')" />
</Target>
<Target Name="OrganizePublishOutput" AfterTargets="PublishOnly">
<ItemGroup>
<PublishRootFiles Include="$(TargetPath)" />
<PublishRootFiles Include="$(TargetPath).config" Condition="Exists('$(TargetPath).config')" />
<PublishLibFiles Include="$(TargetDir)lib\**\*" />
</ItemGroup>
<RemoveDir Directories="$(PublishUrl)" Condition="Exists('$(PublishUrl)')" />
<MakeDir Directories="$(PublishUrl)lib" />
<Copy SourceFiles="@(PublishRootFiles)" DestinationFolder="$(PublishUrl)" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(PublishLibFiles)" DestinationFiles="@(PublishLibFiles->'$(PublishUrl)lib\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="true" />
</Target> </Target>
<PropertyGroup> <PropertyGroup>
<PreBuildEvent> <PreBuildEvent>
+24
View File
@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lithium", "Lithium.csproj", "{B20F7FF2-33B9-1A60-831A-DE88D5488463}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B20F7FF2-33B9-1A60-831A-DE88D5488463}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B20F7FF2-33B9-1A60-831A-DE88D5488463}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B20F7FF2-33B9-1A60-831A-DE88D5488463}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B20F7FF2-33B9-1A60-831A-DE88D5488463}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {751AD755-CC15-4830-AF9B-8C42B731B22E}
EndGlobalSection
EndGlobal
+58 -23
View File
@@ -1,4 +1,4 @@
<Window x:Class="Lithium.MainWindow" <Window x:Class="Lithium.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wv="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf" xmlns:wv="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
@@ -7,6 +7,26 @@
WindowStyle="None" WindowStyle="None"
AllowsTransparency="True"> AllowsTransparency="True">
<Window.Resources>
<Style TargetType="TabItem">
<Setter Property="Foreground" Value="LightGray"/>
<Setter Property="Background" Value="#252525"/>
<Setter Property="BorderBrush" Value="#444"/>
<Setter Property="Padding" Value="12,6"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="#333333"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#2D2D2D"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid> <Grid>
<!-- Custom Title Bar --> <!-- Custom Title Bar -->
<Grid Height="35" VerticalAlignment="Top" MouseDown="TitleBar_MouseDown"> <Grid Height="35" VerticalAlignment="Top" MouseDown="TitleBar_MouseDown">
@@ -96,7 +116,7 @@
<!-- Main Content --> <!-- Main Content -->
<Grid Margin="0,35,0,0"> <Grid Margin="0,35,0,0">
<TabControl Background="#1E1E1E" Foreground="White"> <TabControl x:Name="MainTabControl" Background="#1E1E1E" Foreground="White" SelectionChanged="MainTabControl_SelectionChanged">
<!-- Console Tab --> <!-- Console Tab -->
<TabItem Header="Console"> <TabItem Header="Console">
<Grid Background="#252525" Margin="10"> <Grid Background="#252525" Margin="10">
@@ -173,6 +193,7 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -218,25 +239,34 @@
</Grid> </Grid>
</GroupBox> </GroupBox>
<!-- Jitter Settings --> <!-- Random Offset Settings -->
<GroupBox Grid.Row="2" Grid.ColumnSpan="2" Header="Jitter Settings" Foreground="White" Margin="5" BorderBrush="#444"> <GroupBox Grid.Row="2" Grid.ColumnSpan="2" Header="Random Offset Settings" Foreground="White" Margin="5" BorderBrush="#444">
<Grid Margin="10"> <StackPanel Margin="10">
<Grid.RowDefinitions> <StackPanel Orientation="Horizontal" Margin="0,5">
<RowDefinition Height="Auto"/> <CheckBox x:Name="RandomOffsetEnabledCheckBox" Content="Random Offset Enabled" Foreground="White" VerticalAlignment="Center"/>
<RowDefinition Height="Auto"/> <TextBlock Text="(Applies random X/Y offset on each set activation)" Foreground="Gray" FontSize="11" Margin="10,0,0,0" VerticalAlignment="Center"/>
</Grid.RowDefinitions> </StackPanel>
<Grid.ColumnDefinitions> <Grid Margin="0,5">
<ColumnDefinition Width="Auto"/> <Grid.RowDefinitions>
<ColumnDefinition Width="*"/> <RowDefinition Height="Auto"/>
<ColumnDefinition Width="Auto"/> <RowDefinition Height="Auto"/>
<ColumnDefinition Width="*"/> </Grid.RowDefinitions>
</Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Burst Time (ms):" Foreground="White" Margin="0,5,10,5" VerticalAlignment="Center"/> <TextBlock Grid.Row="0" Grid.Column="0" Text="Min Offset:" Foreground="White" Margin="0,5,10,5" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="JitterBurstTimeTextBox" Margin="0,5" Width="80" HorizontalAlignment="Left" Background="#2D2D2D" Foreground="White" BorderBrush="#444"/> <TextBox Grid.Row="0" Grid.Column="1" x:Name="RandomOffsetMinTextBox" Margin="0,5" Width="80" HorizontalAlignment="Left" Background="#2D2D2D" Foreground="White" BorderBrush="#444"/>
<TextBlock Grid.Row="0" Grid.Column="2" Text="Cooldown (ms):" Foreground="White" Margin="20,5,10,5" VerticalAlignment="Center"/> <TextBlock Grid.Row="0" Grid.Column="2" Text="Max Offset:" Foreground="White" Margin="20,5,10,5" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="3" x:Name="JitterCooldownTextBox" Margin="0,5" Width="80" HorizontalAlignment="Left" Background="#2D2D2D" Foreground="White" BorderBrush="#444"/> <TextBox Grid.Row="0" Grid.Column="3" x:Name="RandomOffsetMaxTextBox" Margin="0,5" Width="80" HorizontalAlignment="Left" Background="#2D2D2D" Foreground="White" BorderBrush="#444"/>
</Grid>
<TextBlock Grid.Row="1" Grid.Column="0" Text="(e.g. 0.0001)" Foreground="Gray" FontSize="11" Margin="0,0,10,0" VerticalAlignment="Center"/>
<TextBlock Grid.Row="1" Grid.Column="2" Text="(e.g. 0.001)" Foreground="Gray" FontSize="11" Margin="20,0,10,0" VerticalAlignment="Center"/>
</Grid>
</StackPanel>
</GroupBox> </GroupBox>
<!-- User & Favorites --> <!-- User & Favorites -->
@@ -267,7 +297,7 @@
<GroupBox Grid.Row="4" Grid.Column="0" Header="Console Settings" Foreground="White" Margin="5" BorderBrush="#444"> <GroupBox Grid.Row="4" Grid.Column="0" Header="Console Settings" Foreground="White" Margin="5" BorderBrush="#444">
<StackPanel Margin="10"> <StackPanel Margin="10">
<TextBlock Text="Text Color:" Foreground="White" Margin="0,5"/> <TextBlock Text="Text Color:" Foreground="White" Margin="0,5"/>
<ComboBox x:Name="TextColorComboBox" Width="120" HorizontalAlignment="Left" Background="#2D2D2D" Foreground="Black" BorderBrush="#444"> <ComboBox x:Name="TextColorComboBox" Width="120" HorizontalAlignment="Left" Background="#2D2D2D" Foreground="White" BorderBrush="#444">
<ComboBoxItem Content="White"/> <ComboBoxItem Content="White"/>
<ComboBoxItem Content="Magenta"/> <ComboBoxItem Content="Magenta"/>
<ComboBoxItem Content="Cyan"/> <ComboBoxItem Content="Cyan"/>
@@ -289,8 +319,13 @@
</StackPanel> </StackPanel>
</GroupBox> </GroupBox>
<!-- Quick Swap Bindings -->
<GroupBox Grid.Row="5" Grid.ColumnSpan="2" Header="Quick Swap Bindings (Format: Key1+Key2 => Operator Name)" Foreground="White" Margin="5" BorderBrush="#444">
<TextBox x:Name="QuickSwapsTextBox" AcceptsReturn="True" Height="80" VerticalScrollBarVisibility="Auto" Background="#2D2D2D" Foreground="White" BorderBrush="#444" FontFamily="Consolas" FontSize="11" Margin="10"/>
</GroupBox>
<!-- Key Code Reference --> <!-- Key Code Reference -->
<GroupBox Grid.Row="5" Grid.ColumnSpan="2" Header="Common Virtual Key Codes" Foreground="White" Margin="5" BorderBrush="#444"> <GroupBox Grid.Row="6" Grid.ColumnSpan="2" Header="Common Virtual Key Codes" Foreground="White" Margin="5" BorderBrush="#444">
<ScrollViewer MaxHeight="120" VerticalScrollBarVisibility="Auto"> <ScrollViewer MaxHeight="120" VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="KeyCodeReferenceText" Foreground="LightGray" FontFamily="Consolas" FontSize="10" Margin="10" TextWrapping="Wrap"> <TextBlock x:Name="KeyCodeReferenceText" Foreground="LightGray" FontFamily="Consolas" FontSize="10" Margin="10" TextWrapping="Wrap">
0x01: Left Mouse, 0x02: Right Mouse, 0x04: Middle Mouse 0x01: Left Mouse, 0x02: Right Mouse, 0x04: Middle Mouse
@@ -304,7 +339,7 @@ Full list: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-c
</GroupBox> </GroupBox>
<!-- Status --> <!-- Status -->
<Border Grid.Row="6" Grid.ColumnSpan="2" Background="#2D2D2D" Margin="5" BorderBrush="#444" BorderThickness="1"> <Border Grid.Row="7" Grid.ColumnSpan="2" Background="#2D2D2D" Margin="5" BorderBrush="#444" BorderThickness="1">
<TextBlock x:Name="SettingsStatusText" Foreground="Lime" FontFamily="Consolas" FontSize="11" Margin="10,5" Text="Settings loaded successfully" TextWrapping="Wrap"/> <TextBlock x:Name="SettingsStatusText" Foreground="Lime" FontFamily="Consolas" FontSize="11" Margin="10,5" Text="Settings loaded successfully" TextWrapping="Wrap"/>
</Border> </Border>
+273 -51
View File
@@ -1,10 +1,12 @@
using Lua; using Lua;
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using Microsoft.Web.WebView2.Core;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
@@ -24,6 +26,10 @@ namespace Lithium
// AA0114 // AA0114
private DateTime prevSetAllowed = DateTime.MinValue; private DateTime prevSetAllowed = DateTime.MinValue;
// Track threads for cleanup
private Thread _hotkeyThread;
private Thread _recoilThread;
// AA0115 // AA0115
public MainWindow() public MainWindow()
{ {
@@ -38,25 +44,72 @@ namespace Lithium
); );
// AA0116 // AA0116
Browser.Source = !string.IsNullOrEmpty(OperatorSets.ConfigExportUrl) InitializeBrowser();
? new Uri(OperatorSets.ConfigExportUrl)
: new Uri("https://jj.giggity.gay/exindex.html");
// AA0117 // AA0117
LoadSettingsToUI(); LoadSettingsToUI();
StartThread(HotkeyLoop); _hotkeyThread = StartThread(HotkeyLoop);
StartThread(RecoilLoop); _recoilThread = StartThread(RecoilLoop);
// Clean up on window close
Closing += MainWindow_Closing;
}
// Handle window closing - dispose resources
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
try
{
// Dispose speech synthesizer
Utils.Dispose();
// Stop hotkey handler gracefully
HotkeyHandler.Stop();
// Stop recoil engine gracefully
RecoilEngine.Stop();
}
catch
{
// Ignore cleanup errors
}
}
private async void InitializeBrowser()
{
try
{
CoreWebView2Environment.SetLoaderDllFolderPath(AppPaths.WebViewLoaderDirectory);
Directory.CreateDirectory(AppPaths.WebViewUserDataDirectory);
var environment = await CoreWebView2Environment.CreateAsync(
userDataFolder: AppPaths.WebViewUserDataDirectory);
await Browser.EnsureCoreWebView2Async(environment);
Browser.Source = !string.IsNullOrEmpty(OperatorSets.ConfigExportUrl)
? new Uri(OperatorSets.ConfigExportUrl)
: new Uri("https://jj.scpsmp.site/exindex.html");
}
catch (WebView2RuntimeNotFoundException)
{
AppendConsole("WebView2 Runtime is not installed. The Website tab is unavailable.");
}
catch (Exception ex)
{
AppendConsole($"Website initialization failed: {ex.Message}");
}
} }
// AA0118 // AA0118
private void StartThread(ThreadStart action) private Thread StartThread(ThreadStart action)
{ {
var t = new Thread(action) var t = new Thread(action)
{ {
IsBackground = true IsBackground = true
}; };
t.Start(); t.Start();
return t;
} }
// AA0119 // AA0119
@@ -68,6 +121,22 @@ namespace Lithium
// ============================================================ // ============================================================
// SETTINGS MANAGEMENT // SETTINGS MANAGEMENT
// ============================================================ // ============================================================
private Brush GetBrushFromConsoleColor(ConsoleColor color)
{
switch (color)
{
case ConsoleColor.White: return Brushes.White;
case ConsoleColor.Magenta: return Brushes.Magenta;
case ConsoleColor.Cyan: return Brushes.Cyan;
case ConsoleColor.Green: return Brushes.Lime;
case ConsoleColor.Yellow: return Brushes.Yellow;
case ConsoleColor.Red: return Brushes.Red;
case ConsoleColor.Blue: return Brushes.DeepSkyBlue;
case ConsoleColor.Gray: return Brushes.Gray;
default: return Brushes.White;
}
}
// AA0121 // AA0121
private void LoadSettingsToUI() private void LoadSettingsToUI()
{ {
@@ -77,7 +146,7 @@ namespace Lithium
DebitFlagCheckBox.IsChecked = ConfigLoader.debitFlag1; DebitFlagCheckBox.IsChecked = ConfigLoader.debitFlag1;
BetaTestCheckBox.IsChecked = ConfigLoader.betaTest; BetaTestCheckBox.IsChecked = ConfigLoader.betaTest;
TtsEnabledCheckBox.IsChecked = ConfigLoader.ttsEnabled; TtsEnabledCheckBox.IsChecked = ConfigLoader.ttsEnabled;
JitterEnabledCheckBox.IsChecked = ConfigLoader.jitterEnabled; RandomOffsetEnabledCheckBox.IsChecked = ConfigLoader.randomOffsetEnabled;
// AA0123 // AA0123
NextSetKeyTextBox.Text = $"0x{ConfigLoader.nextSetKey:X2}"; NextSetKeyTextBox.Text = $"0x{ConfigLoader.nextSetKey:X2}";
@@ -85,25 +154,36 @@ namespace Lithium
RefreshKeyTextBox.Text = $"0x{ConfigLoader.refreshKey:X2}"; RefreshKeyTextBox.Text = $"0x{ConfigLoader.refreshKey:X2}";
SwitchSideKeyTextBox.Text = $"0x{ConfigLoader.switchSideKey:X2}"; SwitchSideKeyTextBox.Text = $"0x{ConfigLoader.switchSideKey:X2}";
// AA0124 // AA0124 - Random Offset Settings
JitterBurstTimeTextBox.Text = ConfigLoader.jitterBurstTime.ToString(); RandomOffsetMinTextBox.Text = ConfigLoader.randomOffsetMin.ToString("0.0000", System.Globalization.CultureInfo.InvariantCulture);
JitterCooldownTextBox.Text = ConfigLoader.jitterCooldown.ToString(); RandomOffsetMaxTextBox.Text = ConfigLoader.randomOffsetMax.ToString("0.0000", System.Globalization.CultureInfo.InvariantCulture);
// AA0125 // AA0125
UserNameTextBox.Text = ConfigLoader.userName ?? ""; UserNameTextBox.Text = ConfigLoader.userName ?? "";
FavoriteAttackersTextBox.Text = ConfigLoader.favoriteAttackers ?? ""; FavoriteAttackersTextBox.Text = ConfigLoader.favoriteAttackers ?? "";
FavoriteDefendersTextBox.Text = ConfigLoader.favoriteDefenders ?? ""; FavoriteDefendersTextBox.Text = ConfigLoader.favoriteDefenders ?? "";
// AA0126 var qsLines = new System.Collections.Generic.List<string>();
foreach (var qs in ConfigLoader.quickSwaps)
{
string keysStr = string.Join("+", qs.Keys.Select(k => $"0x{k:X2}"));
qsLines.Add($"{keysStr} => {qs.TargetOperator}");
}
QuickSwapsTextBox.Text = string.Join(Environment.NewLine, qsLines);
// AA0126 - Fixed: Properly match ConsoleColor enum values
string colorName = ConfigLoader.textColor.ToString();
foreach (ComboBoxItem item in TextColorComboBox.Items) foreach (ComboBoxItem item in TextColorComboBox.Items)
{ {
if (item.Content.ToString() == ConfigLoader.textColor.ToString()) if (item.Content.ToString().Equals(colorName, StringComparison.OrdinalIgnoreCase))
{ {
TextColorComboBox.SelectedItem = item; TextColorComboBox.SelectedItem = item;
break; break;
} }
} }
ConsoleOutput.Foreground = GetBrushFromConsoleColor(ConfigLoader.textColor);
SettingsStatusText.Text = "Settings loaded successfully"; SettingsStatusText.Text = "Settings loaded successfully";
SettingsStatusText.Foreground = new SolidColorBrush(Colors.Lime); SettingsStatusText.Foreground = new SolidColorBrush(Colors.Lime);
} }
@@ -121,7 +201,15 @@ namespace Lithium
try try
{ {
string configContent = GenerateConfigContent(); string configContent = GenerateConfigContent();
File.WriteAllText("init.config", configContent); if (configContent == null)
{
SettingsStatusText.Text = "Error: Invalid configuration values";
SettingsStatusText.Foreground = new SolidColorBrush(Colors.Red);
return;
}
string configPath = AppPaths.GetUserFilePath("init.config");
File.WriteAllText(configPath, configContent);
// AA0129 // AA0129
ConfigLoader.LoadInitConfig(); ConfigLoader.LoadInitConfig();
@@ -131,6 +219,16 @@ namespace Lithium
SettingsStatusText.Foreground = new SolidColorBrush(Colors.Lime); SettingsStatusText.Foreground = new SolidColorBrush(Colors.Lime);
AppendConsole("Settings updated from UI"); AppendConsole("Settings updated from UI");
} }
catch (UnauthorizedAccessException ex)
{
SettingsStatusText.Text = $"Access denied: {ex.Message}";
SettingsStatusText.Foreground = new SolidColorBrush(Colors.Red);
}
catch (IOException ex)
{
SettingsStatusText.Text = $"IO Error: {ex.Message}";
SettingsStatusText.Foreground = new SolidColorBrush(Colors.Red);
}
catch (Exception ex) catch (Exception ex)
{ {
// AA0130 // AA0130
@@ -156,18 +254,20 @@ namespace Lithium
ConfigLoader.debitFlag1 = false; ConfigLoader.debitFlag1 = false;
ConfigLoader.betaTest = false; ConfigLoader.betaTest = false;
ConfigLoader.ttsEnabled = true; ConfigLoader.ttsEnabled = true;
ConfigLoader.jitterEnabled = false; ConfigLoader.randomOffsetEnabled = false;
ConfigLoader.randomOffsetMin = 0.0001;
ConfigLoader.randomOffsetMax = 0.001;
ConfigLoader.nextSetKey = 0x05; ConfigLoader.nextSetKey = 0x05;
ConfigLoader.prevSetKey = 0x06; ConfigLoader.prevSetKey = 0x06;
ConfigLoader.refreshKey = 0x50; ConfigLoader.refreshKey = 0x50;
ConfigLoader.switchSideKey = 0x4D; ConfigLoader.switchSideKey = 0x4D;
ConfigLoader.jitterBurstTime = 1800;
ConfigLoader.jitterCooldown = 3000;
ConfigLoader.userName = null; ConfigLoader.userName = null;
ConfigLoader.favoriteAttackers = null; ConfigLoader.favoriteAttackers = null;
ConfigLoader.favoriteDefenders = null; ConfigLoader.favoriteDefenders = null;
ConfigLoader.textColor = System.ConsoleColor.Magenta; ConfigLoader.textColor = System.ConsoleColor.Magenta;
QuickSwapsTextBox.Text = "";
LoadSettingsToUI(); LoadSettingsToUI();
SettingsStatusText.Text = "Settings reset to defaults (save to apply)"; SettingsStatusText.Text = "Settings reset to defaults (save to apply)";
SettingsStatusText.Foreground = new SolidColorBrush(Colors.Yellow); SettingsStatusText.Foreground = new SolidColorBrush(Colors.Yellow);
@@ -176,42 +276,104 @@ namespace Lithium
// AA0134 // AA0134
private string GenerateConfigContent() private string GenerateConfigContent()
{ {
return $@"-- Lithium Configuration File // Validate and parse random offset values
-- Generated from UI on {DateTime.Now} if (!double.TryParse(RandomOffsetMinTextBox.Text, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double randomOffsetMin))
{
AppendConsole("Error: Invalid random offset min value");
return null;
}
if (!double.TryParse(RandomOffsetMaxTextBox.Text, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double randomOffsetMax))
{
AppendConsole("Error: Invalid random offset max value");
return null;
}
-- Basic Settings // Parse Quick Swaps from UI text box
debitFlag1 = {DebitFlagCheckBox.IsChecked.ToString().ToLower()} ConfigLoader.quickSwaps.Clear();
betaTest = {BetaTestCheckBox.IsChecked.ToString().ToLower()} var textLines = QuickSwapsTextBox.Text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
ttsEnabled = {TtsEnabledCheckBox.IsChecked.ToString().ToLower()} foreach (var rawLine in textLines)
jitterEnabled = {JitterEnabledCheckBox.IsChecked.ToString().ToLower()} {
var line = rawLine.Trim();
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("--") || line.StartsWith("#")) continue;
-- Key Bindings (Virtual Key Codes) string parsedLine = line;
nextSetKey = {ParseKeyCode(NextSetKeyTextBox.Text)} if (!line.StartsWith("quickSwap", StringComparison.OrdinalIgnoreCase))
prevSetKey = {ParseKeyCode(PrevSetKeyTextBox.Text)} {
refreshKey = {ParseKeyCode(RefreshKeyTextBox.Text)} parsedLine = "quickSwap = " + line;
switchSideKey = {ParseKeyCode(SwitchSideKeyTextBox.Text)} }
-- Jitter Settings if (Helper.ParseQuickSwap(parsedLine, out var qsKeys, out var qsOp))
jitterBurstTime = {int.Parse(JitterBurstTimeTextBox.Text)} {
jitterCooldown = {int.Parse(JitterCooldownTextBox.Text)} ConfigLoader.quickSwaps.Add(new QuickSwapBinding { Keys = qsKeys, TargetOperator = qsOp });
}
else
{
AppendConsole($"Warning: Invalid quick swap format: {line}");
}
}
-- User & Favorites var sb = new StringBuilder();
userName = ""{UserNameTextBox.Text}"" sb.AppendLine("-- Lithium Configuration File");
favoriteAttackers = ""{FavoriteAttackersTextBox.Text}"" sb.AppendLine($"-- Generated from UI on {DateTime.Now}");
favoriteDefenders = ""{FavoriteDefendersTextBox.Text}"" sb.AppendLine();
sb.AppendLine("-- Basic Settings");
sb.AppendLine($"debitFlag1 = {DebitFlagCheckBox.IsChecked.ToString().ToLower()}");
sb.AppendLine($"betaTest = {BetaTestCheckBox.IsChecked.ToString().ToLower()}");
sb.AppendLine($"ttsEnabled = {TtsEnabledCheckBox.IsChecked.ToString().ToLower()}");
sb.AppendLine($"randomOffsetEnabled = {RandomOffsetEnabledCheckBox.IsChecked.ToString().ToLower()}");
sb.AppendLine();
sb.AppendLine("-- Key Bindings (Virtual Key Codes)");
sb.AppendLine($"nextSetKey = {ParseKeyCode(NextSetKeyTextBox.Text)}");
sb.AppendLine($"prevSetKey = {ParseKeyCode(PrevSetKeyTextBox.Text)}");
sb.AppendLine($"refreshKey = {ParseKeyCode(RefreshKeyTextBox.Text)}");
sb.AppendLine($"switchSideKey = {ParseKeyCode(SwitchSideKeyTextBox.Text)}");
sb.AppendLine();
sb.AppendLine("-- Random Offset Settings");
sb.AppendLine($"randomOffsetMin = {randomOffsetMin.ToString("0.0000", System.Globalization.CultureInfo.InvariantCulture)}");
sb.AppendLine($"randomOffsetMax = {randomOffsetMax.ToString("0.0000", System.Globalization.CultureInfo.InvariantCulture)}");
sb.AppendLine();
sb.AppendLine("-- User & Favorites");
sb.AppendLine($"userName = \"{UserNameTextBox.Text}\"");
sb.AppendLine($"favoriteAttackers = \"{FavoriteAttackersTextBox.Text}\"");
sb.AppendLine($"favoriteDefenders = \"{FavoriteDefendersTextBox.Text}\"");
sb.AppendLine();
sb.AppendLine("-- Console Settings");
sb.AppendLine($"textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString() ?? "Magenta"}");
-- Console Settings if (ConfigLoader.quickSwaps.Count > 0)
textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString() ?? "Magenta"}"; {
sb.AppendLine();
sb.AppendLine("-- Quick Swap Bindings");
foreach (var qs in ConfigLoader.quickSwaps)
{
string keysStr = string.Join("+", qs.Keys.Select(k => $"0x{k:X2}"));
sb.AppendLine($"quickSwap = {keysStr} => {qs.TargetOperator}");
}
}
return sb.ToString();
} }
// AA0135 // AA0135
private int ParseKeyCode(string text) private int ParseKeyCode(string text)
{ {
if (text.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) if (string.IsNullOrWhiteSpace(text))
return 0x05; // Default key
try
{ {
return Convert.ToInt32(text, 16); if (text.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
return Convert.ToInt32(text, 16);
}
if (int.TryParse(text, out int value))
return value;
} }
return int.Parse(text); catch
{
// Return default on error
}
return 0x05; // Default key
} }
// ============================================================ // ============================================================
@@ -278,6 +440,14 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
Utils.SafeSpeak(OperatorSets.CurrentSet); Utils.SafeSpeak(OperatorSets.CurrentSet);
} }
public void HandleQuickSwap(string targetOp)
{
OperatorSets.QuickSwapTo(targetOp);
AppendConsole($"Quick Swap → {OperatorSets.CurrentSet}");
Utils.SafeSpeak(OperatorSets.CurrentSet);
}
// ============================================================ // ============================================================
// UI CONSOLE // UI CONSOLE
// ============================================================ // ============================================================
@@ -305,7 +475,40 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
private void ToggleCustomSet_Click(object sender, RoutedEventArgs e) private void ToggleCustomSet_Click(object sender, RoutedEventArgs e)
{ {
customSetEnabled = !customSetEnabled; customSetEnabled = !customSetEnabled;
UpdateCustomSetConsole(customSetEnabled ? "Set is ON" : "Set is OFF"); UpdateCustomSetState();
}
private void MainTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateCustomSetState();
}
private void UpdateCustomSetState()
{
var selectedTab = MainTabControl.SelectedItem as TabItem;
bool onCustomTab = selectedTab != null &&
string.Equals(selectedTab.Header?.ToString(), "Custom Set Builder", StringComparison.OrdinalIgnoreCase);
if (customSetEnabled && onCustomTab)
{
if (double.TryParse(RecoilXTextBox.Text, System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out double x) &&
double.TryParse(RecoilYTextBox.Text, System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out double y))
{
RecoilEngine.CustomModeActive = true;
RecoilEngine.CustomRecoilX = x;
RecoilEngine.CustomRecoilY = y;
UpdateCustomSetConsole($"Custom Set ACTIVE | X:{x} Y:{y}");
return;
}
UpdateCustomSetConsole("Error: Invalid recoil values");
}
else
{
RecoilEngine.CustomModeActive = false;
UpdateCustomSetConsole(customSetEnabled ? "Set is ON (switch to tab to activate)" : "Set is OFF");
}
} }
// AA0145 // AA0145
@@ -337,7 +540,7 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
string json = JsonSerializer.Serialize(payload); string json = JsonSerializer.Serialize(payload);
var response = await client.PostAsync( var response = await client.PostAsync(
"https://jj.giggity.gay/api/global/add", "https://jj.scpsmp.site/api/global/add",
new StringContent(json, Encoding.UTF8, "application/json") new StringContent(json, Encoding.UTF8, "application/json")
); );
@@ -362,16 +565,25 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
{ {
try try
{ {
string customSetPath = AppPaths.GetUserFilePath("custom_set.txt");
File.WriteAllText( File.WriteAllText(
"custom_set.txt", customSetPath,
$"{OperatorNameTextBox.Text}|{RecoilXTextBox.Text}|{RecoilYTextBox.Text}|{(AttackerRadioButton.IsChecked == true ? "attacker" : "defender")}" $"{OperatorNameTextBox.Text}|{RecoilXTextBox.Text}|{RecoilYTextBox.Text}|{(AttackerRadioButton.IsChecked == true ? "attacker" : "defender")}"
); );
UpdateCustomSetConsole("Custom set saved"); UpdateCustomSetConsole("Custom set saved");
} }
catch catch (UnauthorizedAccessException)
{ {
UpdateCustomSetConsole("Failed to save custom set"); UpdateCustomSetConsole("Error: Access denied - check file permissions");
}
catch (IOException ex)
{
UpdateCustomSetConsole($"IO Error: {ex.Message}");
}
catch (Exception ex)
{
UpdateCustomSetConsole($"Failed to save custom set: {ex.Message}");
} }
} }
@@ -380,15 +592,17 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
{ {
try try
{ {
if (!File.Exists("custom_set.txt")) string customSetPath = AppPaths.GetReadableFilePath("custom_set.txt");
if (customSetPath == null)
{ {
UpdateCustomSetConsole("No saved set found"); UpdateCustomSetConsole("No saved set found");
return; return;
} }
string[] parts = File.ReadAllText("custom_set.txt").Split('|'); string[] parts = File.ReadAllText(customSetPath).Split('|');
if (parts.Length != 4) if (parts.Length != 4)
throw new Exception("Invalid file"); throw new Exception("Invalid file format");
OperatorNameTextBox.Text = parts[0]; OperatorNameTextBox.Text = parts[0];
RecoilXTextBox.Text = parts[1]; RecoilXTextBox.Text = parts[1];
@@ -401,9 +615,17 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
UpdateCustomSetConsole("Custom set loaded"); UpdateCustomSetConsole("Custom set loaded");
} }
catch catch (UnauthorizedAccessException)
{ {
UpdateCustomSetConsole("Failed to load custom set"); UpdateCustomSetConsole("Error: Access denied - check file permissions");
}
catch (IOException ex)
{
UpdateCustomSetConsole($"IO Error: {ex.Message}");
}
catch (Exception ex)
{
UpdateCustomSetConsole($"Failed to load custom set: {ex.Message}");
} }
} }
+141 -28
View File
@@ -1,7 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
namespace Lua namespace Lua
{ {
@@ -31,27 +32,31 @@ namespace Lua
// AA0058 // AA0058
public static bool onAttackers = true; public static bool onAttackers = true;
private static string CacheDirectory => public static string originalSetBeforeQuickSwap = null;
System.IO.Path.Combine( public static bool isQuickSwapping = false;
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"coax"
);
private static string CacheFile => // Static HttpClient for connection pooling (FIX: previously created new instance each time)
System.IO.Path.Combine(CacheDirectory, "config_cache.txt"); private static readonly HttpClient _httpClient = new HttpClient
{
Timeout = System.TimeSpan.FromSeconds(10)
};
private static string CacheDirectory => AppPaths.UserDataDirectory;
private static string CacheFile => System.IO.Path.Combine(CacheDirectory, "config_cache.txt");
// AA0059 // AA0059
private static string OnlineConfigUrl => private static string OnlineConfigUrl =>
string.IsNullOrEmpty(ConfigLoader.userName) string.IsNullOrEmpty(ConfigLoader.userName)
? null ? null
: $"https://jj.giggity.gay/api/config/export?name={Uri.EscapeDataString(ConfigLoader.userName)}"; : $"https://jj.scpsmp.site/api/config/export?name={Uri.EscapeDataString(ConfigLoader.userName)}";
// AA0060 // AA0060
public static string ConfigExportUrl => public static string ConfigExportUrl =>
string.IsNullOrEmpty(ConfigLoader.userName) string.IsNullOrEmpty(ConfigLoader.userName)
? null ? null
: $"https://jj.giggity.gay/exindex.html?name={Uri.EscapeDataString(ConfigLoader.userName)}"; : $"https://jj.scpsmp.site/exindex.html?name={Uri.EscapeDataString(ConfigLoader.userName)}";
// AA0061 // AA0061
public static string CurrentSet public static string CurrentSet
@@ -108,46 +113,77 @@ namespace Lua
var allLines = new List<string>(); var allLines = new List<string>();
bool loadedOnline = false; bool loadedOnline = false;
// Ensure coax folder exists // Ensure the per-user cache folder exists (with proper error handling)
try { System.IO.Directory.CreateDirectory(CacheDirectory); } catch { } try
{
if (!System.IO.Directory.Exists(CacheDirectory))
System.IO.Directory.CreateDirectory(CacheDirectory);
}
catch
{
// Directory creation failed - will fall back to cache if available
}
// Try loading from online source // Try loading from online source
if (allowOnlineFetch && !string.IsNullOrEmpty(OnlineConfigUrl)) if (allowOnlineFetch && !string.IsNullOrEmpty(OnlineConfigUrl))
{ {
try try
{ {
using (var client = new HttpClient()) string txt = _httpClient.GetStringAsync(OnlineConfigUrl).Result;
if (!string.IsNullOrWhiteSpace(txt))
{ {
string txt = client.GetStringAsync(OnlineConfigUrl).Result; allLines.AddRange(txt.Split('\n'));
if (!string.IsNullOrWhiteSpace(txt)) // Save cache file - with proper error handling
try
{ {
allLines.AddRange(txt.Split('\n')); if (System.IO.Directory.Exists(CacheDirectory))
System.IO.File.WriteAllText(CacheFile, txt);
// Save cache file in per-user coax folder
System.IO.File.WriteAllText(CacheFile, txt);
loadedOnline = true;
} }
catch
{
// Cache write failed, but we have the data in memory
}
loadedOnline = true;
} }
} }
catch (HttpRequestException)
{
// Network error - fall back to cache
}
catch (TaskCanceledException)
{
// Timeout - fall back to cache
}
catch catch
{ {
// Ignore; fallback handled below // Other errors - fall back to cache
} }
} }
// If online fetch failed → fallback to cached file // If online fetch failed → fallback to cached file
if (!loadedOnline && System.IO.File.Exists(CacheFile)) if (!loadedOnline && System.IO.File.Exists(CacheFile))
{ {
var cached = System.IO.File.ReadAllLines(CacheFile); try
if (cached.Length > 0) {
allLines.AddRange(cached); var cached = System.IO.File.ReadAllLines(CacheFile);
if (cached.Length > 0)
allLines.AddRange(cached);
}
catch
{
// Cache file read failed - continue without it
}
} }
// Also load config.txt (local overrides) // Also load config.txt (local overrides)
var local = Helper.ReadLinesSafe("config.txt"); string localConfigPath = AppPaths.GetReadableFilePath("config.txt");
if (local != null && local.Length > 0) var local = localConfigPath == null
? Array.Empty<string>()
: Helper.ReadLinesSafe(localConfigPath);
if (local.Length > 0)
allLines.AddRange(local); allLines.AddRange(local);
bool inAttackers = false; bool inAttackers = false;
@@ -208,6 +244,7 @@ namespace Lua
// AA0070 // AA0070
public static void MergeSets() public static void MergeSets()
{ {
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
if (ConfigLoader.betaTest) if (ConfigLoader.betaTest)
{ {
mergedSets.Clear(); mergedSets.Clear();
@@ -236,6 +273,7 @@ namespace Lua
// AA0071 // AA0071
public static void RestoreSet(string oldSet) public static void RestoreSet(string oldSet)
{ {
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
if (setNames.Count == 0) if (setNames.Count == 0)
{ {
currentSetIndex = 0; currentSetIndex = 0;
@@ -250,6 +288,7 @@ namespace Lua
// AA0072 // AA0072
public static void SwitchSide() public static void SwitchSide()
{ {
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
if (ConfigLoader.betaTest) return; if (ConfigLoader.betaTest) return;
onAttackers = !onAttackers; onAttackers = !onAttackers;
@@ -260,6 +299,7 @@ namespace Lua
// AA0073 // AA0073
public static void NextSet() public static void NextSet()
{ {
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
if (setNames.Count == 0) return; if (setNames.Count == 0) return;
currentSetIndex = (currentSetIndex + 1) % setNames.Count; currentSetIndex = (currentSetIndex + 1) % setNames.Count;
@@ -269,6 +309,7 @@ namespace Lua
// AA0074 // AA0074
public static void PreviousSet() public static void PreviousSet()
{ {
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
if (setNames.Count == 0) return; if (setNames.Count == 0) return;
currentSetIndex = (currentSetIndex - 1 + setNames.Count) % setNames.Count; currentSetIndex = (currentSetIndex - 1 + setNames.Count) % setNames.Count;
@@ -278,7 +319,8 @@ namespace Lua
// AA0075 // AA0075
public static void SetCurrentSet(string name) public static void SetCurrentSet(string name)
{ {
if (setNames == null || setNames.Count == 0) return; if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
if (setNames.Count == 0) return;
int idx = setNames.FindIndex(s => string.Equals(s, name, StringComparison.OrdinalIgnoreCase)); int idx = setNames.FindIndex(s => string.Equals(s, name, StringComparison.OrdinalIgnoreCase));
if (idx >= 0) if (idx >= 0)
@@ -341,5 +383,76 @@ namespace Lua
currentSetIndex = 0; currentSetIndex = 0;
CurrentOperator = CurrentSet; CurrentOperator = CurrentSet;
} }
public static void QuickSwapTo(string targetOp)
{
if (setNames == null || setNames.Count == 0) return;
// Check if the target operator is valid
bool inAttackers = attackers.ContainsKey(targetOp);
bool inDefenders = defenders.ContainsKey(targetOp);
if (!inAttackers && !inDefenders)
{
if (!mergedSets.ContainsKey(targetOp))
{
return; // Invalid operator
}
}
isQuickSwapping = true;
try
{
string preSwitchSet = CurrentSet;
if (!ConfigLoader.betaTest)
{
if (inAttackers && !onAttackers)
{
SwitchSide();
}
else if (inDefenders && onAttackers)
{
SwitchSide();
}
}
if (string.Equals(CurrentSet, targetOp, StringComparison.OrdinalIgnoreCase))
{
if (!string.IsNullOrEmpty(originalSetBeforeQuickSwap))
{
bool origInAttackers = attackers.ContainsKey(originalSetBeforeQuickSwap);
bool origInDefenders = defenders.ContainsKey(originalSetBeforeQuickSwap);
if (!ConfigLoader.betaTest)
{
if (origInAttackers && !onAttackers)
{
SwitchSide();
}
else if (origInDefenders && onAttackers)
{
SwitchSide();
}
}
SetCurrentSet(originalSetBeforeQuickSwap);
originalSetBeforeQuickSwap = null;
}
}
else
{
if (string.IsNullOrEmpty(originalSetBeforeQuickSwap))
{
originalSetBeforeQuickSwap = preSwitchSet;
}
SetCurrentSet(targetOp);
}
}
finally
{
isQuickSwapping = false;
}
}
} }
} }
+86 -63
View File
@@ -1,96 +1,109 @@
using Lithium; using Lithium;
using System; using System;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace Lua namespace Lua
{ {
internal static class RecoilEngine internal static class RecoilEngine
{ {
// AA0077
private const int VK_LBUTTON = 0x01; private const int VK_LBUTTON = 0x01;
// AA0078
private const int VK_RBUTTON = 0x02; private const int VK_RBUTTON = 0x02;
// AA0079 [DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey);
private static readonly Random _rng = new Random(); private static readonly Random _rng = new Random();
// AA0080 private static bool _wasBothDown = false;
private static bool _jitterActive = false; private static double _randomOffsetX = 0;
// AA0081 private static double _randomOffsetY = 0;
private static int _jitterStart = 0;
// AA0082
private static int _lastJitterEnd = 0;
// AA0083
private static double _remainderX, _remainderY; private static double _remainderX, _remainderY;
// AA0084
private static double _jitterMultiplierX, _jitterMultiplierY;
// AA0085 public static bool CustomModeActive = false;
public static void Start(MainWindow window) public static double CustomRecoilX = 0;
public static double CustomRecoilY = 0;
private static bool IsDown(int key)
{ {
while (true) return (GetAsyncKeyState(key) & 0x8000) != 0;
{ }
if (window.IsRecoilEnabled && Utils.IsPressed(VK_LBUTTON) && Utils.IsPressed(VK_RBUTTON))
{
HandleRecoilTick();
}
else
{
_remainderX = _remainderY = 0;
_jitterActive = false;
}
Thread.Sleep(2); 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}");
} }
} }
// AA0086 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() private static void HandleRecoilTick()
{ {
if (!Utils.IsPressed(VK_LBUTTON)) return; try
string setName = OperatorSets.CurrentSet;
if (!OperatorSets.mergedSets.TryGetValue(setName, out var recoil)) return;
int now = Environment.TickCount;
HandleJitter(now);
ApplyRecoil(recoil);
}
// AA0087
private static void HandleJitter(int now)
{
if (!ConfigLoader.jitterEnabled) return;
if (!_jitterActive && now - _lastJitterEnd >= ConfigLoader.jitterCooldown)
{ {
_jitterActive = true; if (CustomModeActive)
_jitterStart = now; {
ApplyRecoil((CustomRecoilX, CustomRecoilY));
return;
}
// AA0088 string setName = OperatorSets.CurrentSet;
_jitterMultiplierX = (_rng.NextDouble() - 0.5) * 0.2;
_jitterMultiplierY = (_rng.NextDouble() - 0.5) * 0.2; if (!OperatorSets.mergedSets.TryGetValue(setName, out var recoil)) return;
ApplyRecoil(recoil);
} }
catch (Exception ex)
if (_jitterActive && now - _jitterStart >= ConfigLoader.jitterBurstTime)
{ {
_jitterActive = false; System.Diagnostics.Debug.WriteLine($"HandleRecoilTick error: {ex.Message}");
_lastJitterEnd = now;
_jitterMultiplierX = _jitterMultiplierY = 0;
} }
} }
// AA0089
private static void ApplyRecoil((double recoilX, double recoilY) recoil) private static void ApplyRecoil((double recoilX, double recoilY) recoil)
{ {
double moveX = recoil.recoilX; double moveX = recoil.recoilX + _randomOffsetX;
double moveY = recoil.recoilY; double moveY = recoil.recoilY + _randomOffsetY;
moveX += recoil.recoilX * _jitterMultiplierX;
moveY += recoil.recoilY * _jitterMultiplierY;
// AA0090
if (ConfigLoader.debitFlag1) if (ConfigLoader.debitFlag1)
{ {
moveX *= 1.282; moveX *= 1.282;
@@ -108,5 +121,15 @@ namespace Lua
Utils.MoveMouse(ix, iy); Utils.MoveMouse(ix, iy);
} }
public static void Stop()
{
try
{
_cancellationSource?.Cancel();
_cancellationSource?.Dispose();
}
catch { }
}
} }
} }
+21 -1
View File
@@ -7,10 +7,30 @@ namespace Lua
static class Utils static class Utils
{ {
// AA0091 // AA0091
public static readonly SpeechSynthesizer Tts = new SpeechSynthesizer(); private static SpeechSynthesizer _tts;
public static SpeechSynthesizer Tts
{
get
{
if (_tts == null)
_tts = new SpeechSynthesizer();
return _tts;
}
}
// AA0092 // AA0092
private static readonly object TtsLock = new object(); private static readonly object TtsLock = new object();
// Cleanup on application shutdown
public static void Dispose()
{
lock (TtsLock)
{
_tts?.Dispose();
_tts = null;
}
}
// AA0093 // AA0093
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern short GetAsyncKeyState(int vKey); public static extern short GetAsyncKeyState(int vKey);
Binary file not shown.
+71 -4
View File
@@ -1,4 +1,4 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -66,6 +66,20 @@ namespace Lua
return int.TryParse(rhs, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); return int.TryParse(rhs, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
} }
// AA0034b
public static bool ParseDoubleFromAssignment(string line, string key, out double 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 double.TryParse(rhs, NumberStyles.Float, CultureInfo.InvariantCulture, out result);
}
// AA0035 // AA0035
public static bool ParseStringFromAssignment(string line, string key, out string result) public static bool ParseStringFromAssignment(string line, string key, out string result)
{ {
@@ -78,9 +92,9 @@ namespace Lua
result = line.Substring(idx + 1).Trim(); result = line.Substring(idx + 1).Trim();
// AA0036 // AA0036 - Fixed: Check length before substring to avoid negative index
if ((result.StartsWith("\"") && result.EndsWith("\"")) || if (result.Length >= 2 && ((result.StartsWith("\"") && result.EndsWith("\"")) ||
(result.StartsWith("'") && result.EndsWith("'"))) (result.StartsWith("'") && result.EndsWith("'"))))
{ {
result = result.Substring(1, result.Length - 2); result = result.Substring(1, result.Length - 2);
} }
@@ -124,5 +138,58 @@ namespace Lua
if (idx >= size) return size - 1; if (idx >= size) return size - 1;
return idx; 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;
}
} }
} }
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.IO" version="4.3.0" targetFramework="net48" />
<package id="System.Net.Http" version="4.3.4" targetFramework="net48" />
<package id="System.Runtime" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net48" />
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net48" />
</packages>
+3197
View File
File diff suppressed because it is too large Load Diff