Compare commits
8 Commits
0.01
...
19556f2d26
| Author | SHA1 | Date | |
|---|---|---|---|
| 19556f2d26 | |||
| 5f993f98b8 | |||
| ae3b4164f3 | |||
| 86c231773d | |||
| b6037e0c91 | |||
| 60b519335f | |||
| 7d6bdc592f | |||
| d6d8a60b20 |
+14
@@ -17,6 +17,9 @@
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Bb]uild/
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
@@ -228,6 +231,17 @@ _pkginfo.txt
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Runtime-generated application data
|
||||
*.exe.WebView2/
|
||||
|
||||
# Local release archives
|
||||
*.7z
|
||||
*.zip
|
||||
|
||||
# Local Claude Code and VS Code settings
|
||||
.claude/
|
||||
.vscode/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
|
||||
@@ -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
|
||||
Binary file not shown.
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"dotnet.preferCSharpExtension": true
|
||||
}
|
||||
+10
-7
@@ -1,8 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<probing privatePath="lib;dependencies;bin" />
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
<startup useLegacyV2RuntimeActivationPolicy="true">
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<probing privatePath="lib" />
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<Application x:Class="Lithium.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="MainWindow.xaml">
|
||||
StartupUri="../MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
<package id="System.Memory" version="4.6.3" 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.Speech" 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.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
|
||||
|
||||
@@ -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
@@ -1,10 +1,19 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Lua
|
||||
{
|
||||
public class QuickSwapBinding
|
||||
{
|
||||
public int[] Keys { get; set; }
|
||||
public string TargetOperator { get; set; }
|
||||
public bool IsHeld { get; set; }
|
||||
}
|
||||
|
||||
internal static class ConfigLoader
|
||||
{
|
||||
public static System.Collections.Generic.List<QuickSwapBinding> quickSwaps = new System.Collections.Generic.List<QuickSwapBinding>();
|
||||
|
||||
// AA0001
|
||||
public static bool debitFlag1 = false;
|
||||
// AA0002
|
||||
@@ -33,11 +42,11 @@ namespace Lua
|
||||
public static string userName = null;
|
||||
|
||||
// AA0013
|
||||
public static bool jitterEnabled = false;
|
||||
public static bool randomOffsetEnabled = false;
|
||||
// AA0014
|
||||
public static int jitterBurstTime = 1800;
|
||||
public static double randomOffsetMin = 0.0001;
|
||||
// AA0015
|
||||
public static int jitterCooldown = 3000;
|
||||
public static double randomOffsetMax = 0.001;
|
||||
|
||||
// AA0016
|
||||
public static ConsoleColor textColor = ConsoleColor.Magenta;
|
||||
@@ -54,17 +63,19 @@ namespace Lua
|
||||
favoriteAttackers = null;
|
||||
favoriteDefenders = null;
|
||||
userName = null;
|
||||
jitterEnabled = false;
|
||||
jitterBurstTime = 1800;
|
||||
jitterCooldown = 3000;
|
||||
randomOffsetEnabled = false;
|
||||
randomOffsetMin = 0.0001;
|
||||
randomOffsetMax = 0.001;
|
||||
textColor = ConsoleColor.Magenta;
|
||||
quickSwaps.Clear();
|
||||
|
||||
SetConsoleColor();
|
||||
|
||||
// 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
|
||||
foreach (var raw in lines)
|
||||
@@ -98,16 +109,18 @@ namespace Lua
|
||||
favoriteDefenders = s2;
|
||||
else if (Helper.ParseStringFromAssignment(line, "userName", out var s3))
|
||||
userName = s3;
|
||||
// AA0025
|
||||
else if (Helper.ParseBoolFromAssignment(line, "jitterEnabled", out var jb))
|
||||
jitterEnabled = jb;
|
||||
else if (Helper.ParseIntFromAssignment(line, "jitterBurstTime", out var jbT))
|
||||
jitterBurstTime = jbT;
|
||||
else if (Helper.ParseIntFromAssignment(line, "jitterCooldown", out var jbC))
|
||||
jitterCooldown = jbC;
|
||||
// AA0025 - Random Offset Settings
|
||||
else if (Helper.ParseBoolFromAssignment(line, "randomOffsetEnabled", out var roe))
|
||||
randomOffsetEnabled = roe;
|
||||
else if (Helper.ParseDoubleFromAssignment(line, "randomOffsetMin", out var rom))
|
||||
randomOffsetMin = rom;
|
||||
else if (Helper.ParseDoubleFromAssignment(line, "randomOffsetMax", out var roM))
|
||||
randomOffsetMax = roM;
|
||||
// AA0026
|
||||
else if (Helper.ParseConsoleColorFromAssignment(line, "textColor", out var col))
|
||||
textColor = col;
|
||||
else if (Helper.ParseQuickSwap(line, out var qsKeys, out var qsOp))
|
||||
quickSwaps.Add(new QuickSwapBinding { Keys = qsKeys, TargetOperator = qsOp });
|
||||
}
|
||||
|
||||
// AA0027
|
||||
|
||||
+111
-16
@@ -1,4 +1,5 @@
|
||||
using Lua;
|
||||
using Lua;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lithium
|
||||
@@ -12,25 +13,50 @@ namespace Lithium
|
||||
private static bool _nextSetHeld, _prevSetHeld, _refreshHeld, _switchSideHeld, _capsHeld;
|
||||
// AA0043
|
||||
private static MainWindow _mainWindow;
|
||||
|
||||
// AA0044 - Added: Cancellation token for graceful shutdown
|
||||
private static CancellationTokenSource _cancellationSource;
|
||||
|
||||
// AA0044
|
||||
public static void Start(MainWindow window)
|
||||
// AA0045 - Updated: Accept cancellation token
|
||||
public static void Start(MainWindow window, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_mainWindow = window;
|
||||
_cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
|
||||
// AA0045
|
||||
while (true)
|
||||
try
|
||||
{
|
||||
HandleToggleCapsLock();
|
||||
HandleNextSet();
|
||||
HandlePrevSet();
|
||||
HandleRefresh();
|
||||
HandleSwitchSide();
|
||||
Thread.Sleep(8);
|
||||
while (!_cancellationSource.Token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
HandleToggleCapsLock();
|
||||
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 - Static method to stop the handler gracefully
|
||||
public static void Stop()
|
||||
{
|
||||
_cancellationSource?.Cancel();
|
||||
}
|
||||
|
||||
// AA0046
|
||||
// AA0047
|
||||
private static void HandleToggleCapsLock()
|
||||
{
|
||||
bool capsDown = Utils.IsPressed(CAPSLOCK);
|
||||
@@ -42,7 +68,7 @@ namespace Lithium
|
||||
if (!capsDown) _capsHeld = false;
|
||||
}
|
||||
|
||||
// AA0047
|
||||
// AA0048
|
||||
private static void HandleNextSet()
|
||||
{
|
||||
if (Utils.IsPressed(ConfigLoader.nextSetKey) && !_nextSetHeld)
|
||||
@@ -56,7 +82,7 @@ namespace Lithium
|
||||
}
|
||||
}
|
||||
|
||||
// AA0048
|
||||
// AA0049
|
||||
private static void HandlePrevSet()
|
||||
{
|
||||
if (Utils.IsPressed(ConfigLoader.prevSetKey) && !_prevSetHeld)
|
||||
@@ -70,7 +96,7 @@ namespace Lithium
|
||||
}
|
||||
}
|
||||
|
||||
// AA0049
|
||||
// AA0050
|
||||
private static void HandleRefresh()
|
||||
{
|
||||
if (Utils.IsPressed(ConfigLoader.refreshKey) && !_refreshHeld)
|
||||
@@ -84,7 +110,7 @@ namespace Lithium
|
||||
}
|
||||
}
|
||||
|
||||
// AA0050
|
||||
// AA0051
|
||||
private static void HandleSwitchSide()
|
||||
{
|
||||
if (Utils.IsPressed(ConfigLoader.switchSideKey) && !_switchSideHeld)
|
||||
@@ -97,5 +123,74 @@ namespace Lithium
|
||||
_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
@@ -16,7 +16,7 @@
|
||||
<Deterministic>true</Deterministic>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<PublishUrl>..\Build\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
@@ -62,7 +62,7 @@
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
@@ -72,7 +72,7 @@
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<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">
|
||||
<HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<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">
|
||||
<HintPath>..\packages\System.IO.Pipelines.10.0.0\lib\net462\System.IO.Pipelines.dll</HintPath>
|
||||
</Reference>
|
||||
<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>
|
||||
</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.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>
|
||||
</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">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</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.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>
|
||||
@@ -120,7 +149,6 @@
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
@@ -133,6 +161,7 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="AppPaths.cs" />
|
||||
<Compile Include="HotkeyHandler.cs" />
|
||||
<Compile Include="RecoilEngine.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
@@ -171,6 +200,7 @@
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="A1\packages.config" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<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'))" />
|
||||
</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')" />
|
||||
<ItemGroup>
|
||||
<!-- Include all referenced DLLs -->
|
||||
<ReferenceCopyLocalPaths Include="@(ReferencePath)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="A1\a23ae8c4c6863364165637fb70679eb3.ico" />
|
||||
</ItemGroup>
|
||||
<Target Name="CopyDllsToLib" AfterTargets="Build">
|
||||
<MakeDir Directories="$(OutputPath)lib" />
|
||||
<Copy SourceFiles="@(ReferenceCopyLocalPaths)" DestinationFolder="$(OutputPath)lib" SkipUnchangedFiles="true" />
|
||||
<Target Name="ClearGeneratedLib" BeforeTargets="CopyFilesToOutputDirectory">
|
||||
<RemoveDir Directories="$(TargetDir)lib" Condition="Exists('$(TargetDir)lib')" />
|
||||
</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>
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
|
||||
@@ -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
@@ -1,4 +1,4 @@
|
||||
<Window x:Class="Lithium.MainWindow"
|
||||
<Window x:Class="Lithium.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:wv="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
|
||||
@@ -7,6 +7,26 @@
|
||||
WindowStyle="None"
|
||||
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>
|
||||
<!-- Custom Title Bar -->
|
||||
<Grid Height="35" VerticalAlignment="Top" MouseDown="TitleBar_MouseDown">
|
||||
@@ -96,7 +116,7 @@
|
||||
|
||||
<!-- Main Content -->
|
||||
<Grid Margin="0,35,0,0">
|
||||
<TabControl Background="#1E1E1E" Foreground="White">
|
||||
<TabControl x:Name="MainTabControl" Background="#1E1E1E" Foreground="White" SelectionChanged="MainTabControl_SelectionChanged">
|
||||
<!-- Console Tab -->
|
||||
<TabItem Header="Console">
|
||||
<Grid Background="#252525" Margin="10">
|
||||
@@ -173,6 +193,7 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
@@ -218,25 +239,34 @@
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Jitter Settings -->
|
||||
<GroupBox Grid.Row="2" Grid.ColumnSpan="2" Header="Jitter Settings" Foreground="White" Margin="5" BorderBrush="#444">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<!-- Random Offset Settings -->
|
||||
<GroupBox Grid.Row="2" Grid.ColumnSpan="2" Header="Random Offset Settings" Foreground="White" Margin="5" BorderBrush="#444">
|
||||
<StackPanel Margin="10">
|
||||
<StackPanel Orientation="Horizontal" Margin="0,5">
|
||||
<CheckBox x:Name="RandomOffsetEnabledCheckBox" Content="Random Offset Enabled" Foreground="White" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="(Applies random X/Y offset on each set activation)" Foreground="Gray" FontSize="11" Margin="10,0,0,0" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<Grid Margin="0,5">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<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"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1" x:Name="JitterBurstTimeTextBox" 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"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="3" x:Name="JitterCooldownTextBox" Margin="0,5" Width="80" HorizontalAlignment="Left" Background="#2D2D2D" Foreground="White" BorderBrush="#444"/>
|
||||
</Grid>
|
||||
<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="RandomOffsetMinTextBox" Margin="0,5" Width="80" HorizontalAlignment="Left" Background="#2D2D2D" Foreground="White" BorderBrush="#444"/>
|
||||
<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="RandomOffsetMaxTextBox" Margin="0,5" Width="80" HorizontalAlignment="Left" Background="#2D2D2D" Foreground="White" BorderBrush="#444"/>
|
||||
|
||||
<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>
|
||||
|
||||
<!-- User & Favorites -->
|
||||
@@ -267,7 +297,7 @@
|
||||
<GroupBox Grid.Row="4" Grid.Column="0" Header="Console Settings" Foreground="White" Margin="5" BorderBrush="#444">
|
||||
<StackPanel Margin="10">
|
||||
<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="Magenta"/>
|
||||
<ComboBoxItem Content="Cyan"/>
|
||||
@@ -289,8 +319,13 @@
|
||||
</StackPanel>
|
||||
</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 -->
|
||||
<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">
|
||||
<TextBlock x:Name="KeyCodeReferenceText" Foreground="LightGray" FontFamily="Consolas" FontSize="10" Margin="10" TextWrapping="Wrap">
|
||||
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>
|
||||
|
||||
<!-- 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"/>
|
||||
</Border>
|
||||
|
||||
|
||||
+273
-51
@@ -1,10 +1,12 @@
|
||||
using Lua;
|
||||
using Lua;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
@@ -23,6 +25,10 @@ namespace Lithium
|
||||
private DateTime nextSetAllowed = DateTime.MinValue;
|
||||
// AA0114
|
||||
private DateTime prevSetAllowed = DateTime.MinValue;
|
||||
|
||||
// Track threads for cleanup
|
||||
private Thread _hotkeyThread;
|
||||
private Thread _recoilThread;
|
||||
|
||||
// AA0115
|
||||
public MainWindow()
|
||||
@@ -38,25 +44,72 @@ namespace Lithium
|
||||
);
|
||||
|
||||
// AA0116
|
||||
Browser.Source = !string.IsNullOrEmpty(OperatorSets.ConfigExportUrl)
|
||||
? new Uri(OperatorSets.ConfigExportUrl)
|
||||
: new Uri("https://jj.giggity.gay/exindex.html");
|
||||
InitializeBrowser();
|
||||
|
||||
// AA0117
|
||||
LoadSettingsToUI();
|
||||
|
||||
StartThread(HotkeyLoop);
|
||||
StartThread(RecoilLoop);
|
||||
_hotkeyThread = StartThread(HotkeyLoop);
|
||||
_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
|
||||
private void StartThread(ThreadStart action)
|
||||
private Thread StartThread(ThreadStart action)
|
||||
{
|
||||
var t = new Thread(action)
|
||||
{
|
||||
IsBackground = true
|
||||
};
|
||||
t.Start();
|
||||
return t;
|
||||
}
|
||||
|
||||
// AA0119
|
||||
@@ -68,6 +121,22 @@ namespace Lithium
|
||||
// ============================================================
|
||||
// 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
|
||||
private void LoadSettingsToUI()
|
||||
{
|
||||
@@ -77,7 +146,7 @@ namespace Lithium
|
||||
DebitFlagCheckBox.IsChecked = ConfigLoader.debitFlag1;
|
||||
BetaTestCheckBox.IsChecked = ConfigLoader.betaTest;
|
||||
TtsEnabledCheckBox.IsChecked = ConfigLoader.ttsEnabled;
|
||||
JitterEnabledCheckBox.IsChecked = ConfigLoader.jitterEnabled;
|
||||
RandomOffsetEnabledCheckBox.IsChecked = ConfigLoader.randomOffsetEnabled;
|
||||
|
||||
// AA0123
|
||||
NextSetKeyTextBox.Text = $"0x{ConfigLoader.nextSetKey:X2}";
|
||||
@@ -85,25 +154,36 @@ namespace Lithium
|
||||
RefreshKeyTextBox.Text = $"0x{ConfigLoader.refreshKey:X2}";
|
||||
SwitchSideKeyTextBox.Text = $"0x{ConfigLoader.switchSideKey:X2}";
|
||||
|
||||
// AA0124
|
||||
JitterBurstTimeTextBox.Text = ConfigLoader.jitterBurstTime.ToString();
|
||||
JitterCooldownTextBox.Text = ConfigLoader.jitterCooldown.ToString();
|
||||
// AA0124 - Random Offset Settings
|
||||
RandomOffsetMinTextBox.Text = ConfigLoader.randomOffsetMin.ToString("0.0000", System.Globalization.CultureInfo.InvariantCulture);
|
||||
RandomOffsetMaxTextBox.Text = ConfigLoader.randomOffsetMax.ToString("0.0000", System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
// AA0125
|
||||
UserNameTextBox.Text = ConfigLoader.userName ?? "";
|
||||
FavoriteAttackersTextBox.Text = ConfigLoader.favoriteAttackers ?? "";
|
||||
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)
|
||||
{
|
||||
if (item.Content.ToString() == ConfigLoader.textColor.ToString())
|
||||
if (item.Content.ToString().Equals(colorName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
TextColorComboBox.SelectedItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ConsoleOutput.Foreground = GetBrushFromConsoleColor(ConfigLoader.textColor);
|
||||
|
||||
SettingsStatusText.Text = "Settings loaded successfully";
|
||||
SettingsStatusText.Foreground = new SolidColorBrush(Colors.Lime);
|
||||
}
|
||||
@@ -121,7 +201,15 @@ namespace Lithium
|
||||
try
|
||||
{
|
||||
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
|
||||
ConfigLoader.LoadInitConfig();
|
||||
@@ -131,6 +219,16 @@ namespace Lithium
|
||||
SettingsStatusText.Foreground = new SolidColorBrush(Colors.Lime);
|
||||
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)
|
||||
{
|
||||
// AA0130
|
||||
@@ -156,18 +254,20 @@ namespace Lithium
|
||||
ConfigLoader.debitFlag1 = false;
|
||||
ConfigLoader.betaTest = false;
|
||||
ConfigLoader.ttsEnabled = true;
|
||||
ConfigLoader.jitterEnabled = false;
|
||||
ConfigLoader.randomOffsetEnabled = false;
|
||||
ConfigLoader.randomOffsetMin = 0.0001;
|
||||
ConfigLoader.randomOffsetMax = 0.001;
|
||||
ConfigLoader.nextSetKey = 0x05;
|
||||
ConfigLoader.prevSetKey = 0x06;
|
||||
ConfigLoader.refreshKey = 0x50;
|
||||
ConfigLoader.switchSideKey = 0x4D;
|
||||
ConfigLoader.jitterBurstTime = 1800;
|
||||
ConfigLoader.jitterCooldown = 3000;
|
||||
ConfigLoader.userName = null;
|
||||
ConfigLoader.favoriteAttackers = null;
|
||||
ConfigLoader.favoriteDefenders = null;
|
||||
ConfigLoader.textColor = System.ConsoleColor.Magenta;
|
||||
|
||||
QuickSwapsTextBox.Text = "";
|
||||
|
||||
LoadSettingsToUI();
|
||||
SettingsStatusText.Text = "Settings reset to defaults (save to apply)";
|
||||
SettingsStatusText.Foreground = new SolidColorBrush(Colors.Yellow);
|
||||
@@ -176,42 +276,104 @@ namespace Lithium
|
||||
// AA0134
|
||||
private string GenerateConfigContent()
|
||||
{
|
||||
return $@"-- Lithium Configuration File
|
||||
-- Generated from UI on {DateTime.Now}
|
||||
// Validate and parse random offset values
|
||||
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
|
||||
debitFlag1 = {DebitFlagCheckBox.IsChecked.ToString().ToLower()}
|
||||
betaTest = {BetaTestCheckBox.IsChecked.ToString().ToLower()}
|
||||
ttsEnabled = {TtsEnabledCheckBox.IsChecked.ToString().ToLower()}
|
||||
jitterEnabled = {JitterEnabledCheckBox.IsChecked.ToString().ToLower()}
|
||||
// Parse Quick Swaps from UI text box
|
||||
ConfigLoader.quickSwaps.Clear();
|
||||
var textLines = QuickSwapsTextBox.Text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var rawLine in textLines)
|
||||
{
|
||||
var line = rawLine.Trim();
|
||||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("--") || line.StartsWith("#")) continue;
|
||||
|
||||
-- Key Bindings (Virtual Key Codes)
|
||||
nextSetKey = {ParseKeyCode(NextSetKeyTextBox.Text)}
|
||||
prevSetKey = {ParseKeyCode(PrevSetKeyTextBox.Text)}
|
||||
refreshKey = {ParseKeyCode(RefreshKeyTextBox.Text)}
|
||||
switchSideKey = {ParseKeyCode(SwitchSideKeyTextBox.Text)}
|
||||
string parsedLine = line;
|
||||
if (!line.StartsWith("quickSwap", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
parsedLine = "quickSwap = " + line;
|
||||
}
|
||||
|
||||
-- Jitter Settings
|
||||
jitterBurstTime = {int.Parse(JitterBurstTimeTextBox.Text)}
|
||||
jitterCooldown = {int.Parse(JitterCooldownTextBox.Text)}
|
||||
if (Helper.ParseQuickSwap(parsedLine, out var qsKeys, out var qsOp))
|
||||
{
|
||||
ConfigLoader.quickSwaps.Add(new QuickSwapBinding { Keys = qsKeys, TargetOperator = qsOp });
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendConsole($"Warning: Invalid quick swap format: {line}");
|
||||
}
|
||||
}
|
||||
|
||||
-- User & Favorites
|
||||
userName = ""{UserNameTextBox.Text}""
|
||||
favoriteAttackers = ""{FavoriteAttackersTextBox.Text}""
|
||||
favoriteDefenders = ""{FavoriteDefendersTextBox.Text}""
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("-- Lithium Configuration File");
|
||||
sb.AppendLine($"-- Generated from UI on {DateTime.Now}");
|
||||
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
|
||||
textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString() ?? "Magenta"}";
|
||||
if (ConfigLoader.quickSwaps.Count > 0)
|
||||
{
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
public void HandleQuickSwap(string targetOp)
|
||||
{
|
||||
OperatorSets.QuickSwapTo(targetOp);
|
||||
|
||||
AppendConsole($"Quick Swap → {OperatorSets.CurrentSet}");
|
||||
Utils.SafeSpeak(OperatorSets.CurrentSet);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// UI CONSOLE
|
||||
// ============================================================
|
||||
@@ -305,7 +475,40 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
|
||||
private void ToggleCustomSet_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
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
|
||||
@@ -337,7 +540,7 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
|
||||
string json = JsonSerializer.Serialize(payload);
|
||||
|
||||
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")
|
||||
);
|
||||
|
||||
@@ -362,16 +565,25 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
|
||||
{
|
||||
try
|
||||
{
|
||||
string customSetPath = AppPaths.GetUserFilePath("custom_set.txt");
|
||||
File.WriteAllText(
|
||||
"custom_set.txt",
|
||||
customSetPath,
|
||||
$"{OperatorNameTextBox.Text}|{RecoilXTextBox.Text}|{RecoilYTextBox.Text}|{(AttackerRadioButton.IsChecked == true ? "attacker" : "defender")}"
|
||||
);
|
||||
|
||||
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
|
||||
{
|
||||
if (!File.Exists("custom_set.txt"))
|
||||
string customSetPath = AppPaths.GetReadableFilePath("custom_set.txt");
|
||||
|
||||
if (customSetPath == null)
|
||||
{
|
||||
UpdateCustomSetConsole("No saved set found");
|
||||
return;
|
||||
}
|
||||
|
||||
string[] parts = File.ReadAllText("custom_set.txt").Split('|');
|
||||
string[] parts = File.ReadAllText(customSetPath).Split('|');
|
||||
if (parts.Length != 4)
|
||||
throw new Exception("Invalid file");
|
||||
throw new Exception("Invalid file format");
|
||||
|
||||
OperatorNameTextBox.Text = parts[0];
|
||||
RecoilXTextBox.Text = parts[1];
|
||||
@@ -401,9 +615,17 @@ textColor = {((ComboBoxItem)TextColorComboBox.SelectedItem)?.Content.ToString()
|
||||
|
||||
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
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Lua
|
||||
{
|
||||
@@ -31,27 +32,31 @@ namespace Lua
|
||||
// AA0058
|
||||
public static bool onAttackers = true;
|
||||
|
||||
private static string CacheDirectory =>
|
||||
System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
"coax"
|
||||
);
|
||||
public static string originalSetBeforeQuickSwap = null;
|
||||
public static bool isQuickSwapping = false;
|
||||
|
||||
private static string CacheFile =>
|
||||
System.IO.Path.Combine(CacheDirectory, "config_cache.txt");
|
||||
// Static HttpClient for connection pooling (FIX: previously created new instance each time)
|
||||
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
|
||||
private static string OnlineConfigUrl =>
|
||||
string.IsNullOrEmpty(ConfigLoader.userName)
|
||||
? 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
|
||||
public static string ConfigExportUrl =>
|
||||
string.IsNullOrEmpty(ConfigLoader.userName)
|
||||
? null
|
||||
: $"https://jj.giggity.gay/exindex.html?name={Uri.EscapeDataString(ConfigLoader.userName)}";
|
||||
: $"https://jj.scpsmp.site/exindex.html?name={Uri.EscapeDataString(ConfigLoader.userName)}";
|
||||
|
||||
// AA0061
|
||||
public static string CurrentSet
|
||||
@@ -108,46 +113,77 @@ namespace Lua
|
||||
var allLines = new List<string>();
|
||||
bool loadedOnline = false;
|
||||
|
||||
// Ensure coax folder exists
|
||||
try { System.IO.Directory.CreateDirectory(CacheDirectory); } catch { }
|
||||
// Ensure the per-user cache folder exists (with proper error handling)
|
||||
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
|
||||
if (allowOnlineFetch && !string.IsNullOrEmpty(OnlineConfigUrl))
|
||||
{
|
||||
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'));
|
||||
|
||||
// Save cache file in per-user coax folder
|
||||
System.IO.File.WriteAllText(CacheFile, txt);
|
||||
|
||||
loadedOnline = true;
|
||||
if (System.IO.Directory.Exists(CacheDirectory))
|
||||
System.IO.File.WriteAllText(CacheFile, txt);
|
||||
}
|
||||
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
|
||||
{
|
||||
// Ignore; fallback handled below
|
||||
// Other errors - fall back to cache
|
||||
}
|
||||
}
|
||||
|
||||
// If online fetch failed → fallback to cached file
|
||||
if (!loadedOnline && System.IO.File.Exists(CacheFile))
|
||||
{
|
||||
var cached = System.IO.File.ReadAllLines(CacheFile);
|
||||
if (cached.Length > 0)
|
||||
allLines.AddRange(cached);
|
||||
try
|
||||
{
|
||||
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)
|
||||
var local = Helper.ReadLinesSafe("config.txt");
|
||||
if (local != null && local.Length > 0)
|
||||
string localConfigPath = AppPaths.GetReadableFilePath("config.txt");
|
||||
var local = localConfigPath == null
|
||||
? Array.Empty<string>()
|
||||
: Helper.ReadLinesSafe(localConfigPath);
|
||||
if (local.Length > 0)
|
||||
allLines.AddRange(local);
|
||||
|
||||
bool inAttackers = false;
|
||||
@@ -208,6 +244,7 @@ namespace Lua
|
||||
// AA0070
|
||||
public static void MergeSets()
|
||||
{
|
||||
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
|
||||
if (ConfigLoader.betaTest)
|
||||
{
|
||||
mergedSets.Clear();
|
||||
@@ -236,6 +273,7 @@ namespace Lua
|
||||
// AA0071
|
||||
public static void RestoreSet(string oldSet)
|
||||
{
|
||||
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
|
||||
if (setNames.Count == 0)
|
||||
{
|
||||
currentSetIndex = 0;
|
||||
@@ -250,6 +288,7 @@ namespace Lua
|
||||
// AA0072
|
||||
public static void SwitchSide()
|
||||
{
|
||||
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
|
||||
if (ConfigLoader.betaTest) return;
|
||||
|
||||
onAttackers = !onAttackers;
|
||||
@@ -260,6 +299,7 @@ namespace Lua
|
||||
// AA0073
|
||||
public static void NextSet()
|
||||
{
|
||||
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
|
||||
if (setNames.Count == 0) return;
|
||||
|
||||
currentSetIndex = (currentSetIndex + 1) % setNames.Count;
|
||||
@@ -269,6 +309,7 @@ namespace Lua
|
||||
// AA0074
|
||||
public static void PreviousSet()
|
||||
{
|
||||
if (!isQuickSwapping) originalSetBeforeQuickSwap = null;
|
||||
if (setNames.Count == 0) return;
|
||||
|
||||
currentSetIndex = (currentSetIndex - 1 + setNames.Count) % setNames.Count;
|
||||
@@ -278,7 +319,8 @@ namespace Lua
|
||||
// AA0075
|
||||
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));
|
||||
if (idx >= 0)
|
||||
@@ -341,5 +383,76 @@ namespace Lua
|
||||
currentSetIndex = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+87
-64
@@ -1,96 +1,109 @@
|
||||
using Lithium;
|
||||
using Lithium;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lua
|
||||
{
|
||||
internal static class RecoilEngine
|
||||
{
|
||||
// AA0077
|
||||
private const int VK_LBUTTON = 0x01;
|
||||
// AA0078
|
||||
private const int VK_RBUTTON = 0x02;
|
||||
|
||||
// AA0079
|
||||
[DllImport("user32.dll")]
|
||||
private static extern short GetAsyncKeyState(int vKey);
|
||||
|
||||
private static readonly Random _rng = new Random();
|
||||
// AA0080
|
||||
private static bool _jitterActive = false;
|
||||
// AA0081
|
||||
private static int _jitterStart = 0;
|
||||
// AA0082
|
||||
private static int _lastJitterEnd = 0;
|
||||
// AA0083
|
||||
private static bool _wasBothDown = false;
|
||||
private static double _randomOffsetX = 0;
|
||||
private static double _randomOffsetY = 0;
|
||||
private static double _remainderX, _remainderY;
|
||||
// AA0084
|
||||
private static double _jitterMultiplierX, _jitterMultiplierY;
|
||||
|
||||
// AA0085
|
||||
public static void Start(MainWindow window)
|
||||
public static bool CustomModeActive = false;
|
||||
public static double CustomRecoilX = 0;
|
||||
public static double CustomRecoilY = 0;
|
||||
|
||||
private static bool IsDown(int key)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (window.IsRecoilEnabled && Utils.IsPressed(VK_LBUTTON) && Utils.IsPressed(VK_RBUTTON))
|
||||
{
|
||||
HandleRecoilTick();
|
||||
}
|
||||
else
|
||||
{
|
||||
_remainderX = _remainderY = 0;
|
||||
_jitterActive = false;
|
||||
}
|
||||
return (GetAsyncKeyState(key) & 0x8000) != 0;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (!Utils.IsPressed(VK_LBUTTON)) return;
|
||||
|
||||
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)
|
||||
try
|
||||
{
|
||||
_jitterActive = true;
|
||||
_jitterStart = now;
|
||||
if (CustomModeActive)
|
||||
{
|
||||
ApplyRecoil((CustomRecoilX, CustomRecoilY));
|
||||
return;
|
||||
}
|
||||
|
||||
// AA0088
|
||||
_jitterMultiplierX = (_rng.NextDouble() - 0.5) * 0.2;
|
||||
_jitterMultiplierY = (_rng.NextDouble() - 0.5) * 0.2;
|
||||
string setName = OperatorSets.CurrentSet;
|
||||
|
||||
if (!OperatorSets.mergedSets.TryGetValue(setName, out var recoil)) return;
|
||||
|
||||
ApplyRecoil(recoil);
|
||||
}
|
||||
|
||||
if (_jitterActive && now - _jitterStart >= ConfigLoader.jitterBurstTime)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_jitterActive = false;
|
||||
_lastJitterEnd = now;
|
||||
_jitterMultiplierX = _jitterMultiplierY = 0;
|
||||
System.Diagnostics.Debug.WriteLine($"HandleRecoilTick error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// AA0089
|
||||
private static void ApplyRecoil((double recoilX, double recoilY) recoil)
|
||||
{
|
||||
double moveX = recoil.recoilX;
|
||||
double moveY = recoil.recoilY;
|
||||
double moveX = recoil.recoilX + _randomOffsetX;
|
||||
double moveY = recoil.recoilY + _randomOffsetY;
|
||||
|
||||
moveX += recoil.recoilX * _jitterMultiplierX;
|
||||
moveY += recoil.recoilY * _jitterMultiplierY;
|
||||
|
||||
// AA0090
|
||||
if (ConfigLoader.debitFlag1)
|
||||
{
|
||||
moveX *= 1.282;
|
||||
@@ -108,5 +121,15 @@ namespace Lua
|
||||
|
||||
Utils.MoveMouse(ix, iy);
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
_cancellationSource?.Cancel();
|
||||
_cancellationSource?.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-1
@@ -7,9 +7,29 @@ namespace Lua
|
||||
static class Utils
|
||||
{
|
||||
// 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
|
||||
private static readonly object TtsLock = new object();
|
||||
|
||||
// Cleanup on application shutdown
|
||||
public static void Dispose()
|
||||
{
|
||||
lock (TtsLock)
|
||||
{
|
||||
_tts?.Dispose();
|
||||
_tts = null;
|
||||
}
|
||||
}
|
||||
|
||||
// AA0093
|
||||
[DllImport("user32.dll")]
|
||||
|
||||
Binary file not shown.
+71
-4
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -66,6 +66,20 @@ namespace Lua
|
||||
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
|
||||
public static bool ParseStringFromAssignment(string line, string key, out string result)
|
||||
{
|
||||
@@ -78,9 +92,9 @@ namespace Lua
|
||||
|
||||
result = line.Substring(idx + 1).Trim();
|
||||
|
||||
// AA0036
|
||||
if ((result.StartsWith("\"") && result.EndsWith("\"")) ||
|
||||
(result.StartsWith("'") && result.EndsWith("'")))
|
||||
// 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);
|
||||
}
|
||||
@@ -124,5 +138,58 @@ namespace Lua
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user