mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-27 10:15:50 +08:00
Merge branch 'main' into feat/new_fishing
This commit is contained in:
@@ -61,7 +61,7 @@ internal class HsvTestWindow
|
||||
Cv2.CreateTrackbar("High S", _windowDetectionName, ref _highS, MaxValue, on_high_S_thresh_trackbar);
|
||||
Cv2.CreateTrackbar("Low V", _windowDetectionName, ref _lowV, MaxValue, on_low_V_thresh_trackbar);
|
||||
Cv2.CreateTrackbar("High V", _windowDetectionName, ref _highV, MaxValue, on_high_V_thresh_trackbar);
|
||||
var frame = Cv2.ImRead(@"E:\HuiTask\更好的原神\自动秘境\自动战斗\队伍识别\bsz1.png", ImreadModes.Color);
|
||||
var frame = Cv2.ImRead(@"E:\HuiTask\更好的原神\自动秘境\Q识别\拼图\2.png", ImreadModes.Color);
|
||||
Mat frameHsv = new Mat();
|
||||
// Convert from BGR to HSV colorspace
|
||||
Cv2.CvtColor(frame, frameHsv, ColorConversionCodes.BGR2HSV);
|
||||
|
||||
@@ -185,6 +185,8 @@ public partial class App : Application
|
||||
protected override async void OnExit(ExitEventArgs e)
|
||||
{
|
||||
base.OnExit(e);
|
||||
|
||||
TempManager.CleanUp();
|
||||
|
||||
await _host.StopAsync();
|
||||
_host.Dispose();
|
||||
|
||||
BIN
BetterGenshinImpact/Assets/Images/banner.jpg
Normal file
BIN
BetterGenshinImpact/Assets/Images/banner.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 288 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 136 KiB |
@@ -10,7 +10,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ApplicationIcon>Assets\Images\logo.ico</ApplicationIcon>
|
||||
<AssemblyName>BetterGI</AssemblyName>
|
||||
<AssemblyVersion>0.39.6</AssemblyVersion>
|
||||
<AssemblyVersion>0.40.1</AssemblyVersion>
|
||||
<Platforms>x64</Platforms>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
@@ -48,6 +48,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.ML.OnnxRuntime.DirectML" Version="1.18.1" />
|
||||
<PackageReference Include="Microsoft.ML.OnnxRuntime.Managed" Version="1.18.1" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2592.51" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
|
||||
<PackageReference Include="OpenCvSharp4.WpfExtensions" Version="4.8.0.20230708" />
|
||||
@@ -148,6 +149,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="GameTask\OneDragon\" />
|
||||
<Folder Include="Service\Notification\Builder\" />
|
||||
<Folder Include="User\AutoPathing\" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -39,4 +39,10 @@ public partial class CommonConfig : ObservableObject
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _isFirstRun = true;
|
||||
|
||||
/// <summary>
|
||||
/// 这个版本是否运行过
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private string _runForVersion = string.Empty;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,10 @@ public partial class PathingPartyConfig : ObservableObject
|
||||
[ObservableProperty]
|
||||
private bool _soloTaskUseFightEnabled = false;
|
||||
|
||||
//不在某时执行
|
||||
[ObservableProperty]
|
||||
private string _skipDuring = "";
|
||||
|
||||
// 使用小道具的间隔时间
|
||||
[ObservableProperty]
|
||||
private int _useGadgetIntervalMs = 0;
|
||||
|
||||
@@ -92,6 +92,11 @@ public class ScriptRepoUpdater : Singleton<ScriptRepoUpdater>
|
||||
// 解析信息
|
||||
var fastProxyUrl = res.Item1;
|
||||
var jsonString = res.Item2;
|
||||
if (string.IsNullOrEmpty(jsonString))
|
||||
{
|
||||
throw new Exception("获取仓库信息失败");
|
||||
}
|
||||
|
||||
var (time, url, file) = ParseJson(jsonString);
|
||||
|
||||
var updated = false;
|
||||
@@ -110,6 +115,7 @@ public class ScriptRepoUpdater : Singleton<ScriptRepoUpdater>
|
||||
{
|
||||
needDownload = true;
|
||||
}
|
||||
|
||||
if (needDownload)
|
||||
{
|
||||
await DownloadRepoAndUnzip(string.Format(fastProxyUrl, url));
|
||||
@@ -246,6 +252,7 @@ public class ScriptRepoUpdater : Singleton<ScriptRepoUpdater>
|
||||
Content = $"检测到{(formClipboard ? "剪切板上存在" : "")}脚本订阅链接,解析后需要导入的脚本为:{pathJson}。\n是否导入并覆盖此文件或者文件夹下的脚本?",
|
||||
CloseButtonText = "关闭",
|
||||
PrimaryButtonText = "确认导入",
|
||||
Owner = Application.Current.MainWindow,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
@@ -393,7 +400,7 @@ public class ScriptRepoUpdater : Singleton<ScriptRepoUpdater>
|
||||
{
|
||||
// 目标文件所在文件夹不存在时创建它
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destPath)!);
|
||||
|
||||
|
||||
if (File.Exists(destPath))
|
||||
{
|
||||
File.Delete(destPath);
|
||||
@@ -490,4 +497,4 @@ public class ScriptRepoUpdater : Singleton<ScriptRepoUpdater>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ using BetterGenshinImpact.GameTask.AutoTrackPath;
|
||||
using BetterGenshinImpact.GameTask.Common.BgiVision;
|
||||
using BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||||
using BetterGenshinImpact.GameTask.Common.Job;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using Vanara.PInvoke;
|
||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||
using static Vanara.PInvoke.Kernel32;
|
||||
@@ -43,8 +44,6 @@ public class AutoDomainTask : ISoloTask
|
||||
|
||||
private readonly AutoDomainParam _taskParam;
|
||||
|
||||
private readonly PostMessageSimulator _simulator;
|
||||
|
||||
private readonly YoloV8Predictor _predictor;
|
||||
|
||||
private readonly AutoDomainConfig _config;
|
||||
@@ -55,10 +54,8 @@ public class AutoDomainTask : ISoloTask
|
||||
|
||||
public AutoDomainTask(AutoDomainParam taskParam)
|
||||
{
|
||||
_taskParam = taskParam;
|
||||
AutoFightAssets.DestroyInstance();
|
||||
_simulator = TaskContext.Instance().PostMessageSimulator;
|
||||
|
||||
_taskParam = taskParam;
|
||||
_predictor = YoloV8Builder.CreateDefaultBuilder()
|
||||
.UseOnnxModel(Global.Absolute(@"Assets\Model\Domain\bgi_tree.onnx"))
|
||||
.WithSessionOptions(BgiSessionOption.Instance.Options)
|
||||
@@ -72,9 +69,9 @@ public class AutoDomainTask : ISoloTask
|
||||
public async Task Start(CancellationToken ct)
|
||||
{
|
||||
_ct = ct;
|
||||
|
||||
|
||||
Init();
|
||||
NotificationHelper.SendTaskNotificationWithScreenshotUsing(b => b.Domain().Started().Build()); // TODO: 通知后续需要删除迁移
|
||||
Notify.Event(NotificationEvent.DomainStart).Success("自动秘境启动");
|
||||
|
||||
// 3次复活重试
|
||||
for (int i = 0; i < 3; i++)
|
||||
@@ -91,6 +88,7 @@ public class AutoDomainTask : ISoloTask
|
||||
{
|
||||
Logger.LogWarning("自动秘境:{Text}", "复活后重试秘境...");
|
||||
await Delay(2000, ct);
|
||||
Notify.Event(NotificationEvent.DomainRetry).Error("存在角色死亡,复活后重试秘境...");
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -106,6 +104,7 @@ public class AutoDomainTask : ISoloTask
|
||||
await Delay(2000, ct);
|
||||
|
||||
await ArtifactSalvage();
|
||||
Notify.Event(NotificationEvent.DomainEnd).Success("自动秘境结束");
|
||||
}
|
||||
|
||||
private async Task DoDomain()
|
||||
@@ -161,12 +160,10 @@ public class AutoDomainTask : ISoloTask
|
||||
{
|
||||
Logger.LogInformation("体力已经耗尽,结束自动秘境");
|
||||
}
|
||||
|
||||
NotificationHelper.SendTaskNotificationWithScreenshotUsing(b => b.Domain().Success().Build());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
NotificationHelper.SendTaskNotificationWithScreenshotUsing(b => b.Domain().Progress().Build());
|
||||
Notify.Event(NotificationEvent.DomainReward).Success("自动秘境奖励领取");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +284,7 @@ public class AutoDomainTask : ISoloTask
|
||||
|
||||
private async Task EnterDomain()
|
||||
{
|
||||
var fightAssets = AutoFightContext.Instance.FightAssets;
|
||||
var fightAssets = AutoFightAssets.Instance;
|
||||
|
||||
// 进入秘境
|
||||
for (int i = 0; i < 3; i++) // 3次重试 有时候会拾取晶蝶
|
||||
@@ -299,9 +296,9 @@ public class AutoDomainTask : ISoloTask
|
||||
Logger.LogInformation("自动秘境:{Text}", "进入秘境");
|
||||
// 秘境开门动画 5s
|
||||
await Delay(5000, _ct);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
await Delay(800, _ct);
|
||||
}
|
||||
}
|
||||
@@ -332,7 +329,7 @@ public class AutoDomainTask : ISoloTask
|
||||
while (retryTimes < 120)
|
||||
{
|
||||
retryTimes++;
|
||||
using var cactRectArea = CaptureToRectArea().Find(AutoFightContext.Instance.FightAssets.ClickAnyCloseTipRa);
|
||||
using var cactRectArea = CaptureToRectArea().Find(AutoFightAssets.Instance.ClickAnyCloseTipRa);
|
||||
if (!cactRectArea.IsEmpty())
|
||||
{
|
||||
await Delay(1000, _ct);
|
||||
@@ -368,7 +365,7 @@ public class AutoDomainTask : ISoloTask
|
||||
|
||||
await Task.Run((Action)(() =>
|
||||
{
|
||||
_simulator.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
|
||||
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
|
||||
Sleep(30, _ct);
|
||||
// 组合键好像不能直接用 postmessage
|
||||
if (!_config.WalkToF)
|
||||
@@ -395,7 +392,7 @@ public class AutoDomainTask : ISoloTask
|
||||
}
|
||||
finally
|
||||
{
|
||||
_simulator.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
|
||||
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
|
||||
Sleep(50);
|
||||
if (!_config.WalkToF)
|
||||
{
|
||||
@@ -495,7 +492,7 @@ public class AutoDomainTask : ISoloTask
|
||||
{
|
||||
using var ra = CaptureToRectArea();
|
||||
|
||||
var endTipsRect = ra.DeriveCrop(AutoFightContext.Instance.FightAssets.EndTipsUpperRect);
|
||||
var endTipsRect = ra.DeriveCrop(AutoFightAssets.Instance.EndTipsUpperRect);
|
||||
var text = OcrFactory.Paddle.Ocr(endTipsRect.SrcGreyMat);
|
||||
if (text.Contains("挑战") || text.Contains("达成"))
|
||||
{
|
||||
@@ -503,7 +500,7 @@ public class AutoDomainTask : ISoloTask
|
||||
return true;
|
||||
}
|
||||
|
||||
endTipsRect = ra.DeriveCrop(AutoFightContext.Instance.FightAssets.EndTipsRect);
|
||||
endTipsRect = ra.DeriveCrop(AutoFightAssets.Instance.EndTipsRect);
|
||||
text = OcrFactory.Paddle.Ocr(endTipsRect.SrcGreyMat);
|
||||
if (text.Contains("自动") || text.Contains("退出"))
|
||||
{
|
||||
@@ -611,13 +608,13 @@ public class AutoDomainTask : ISoloTask
|
||||
if (rightKeyDown)
|
||||
{
|
||||
// 先松开D键
|
||||
_simulator.KeyUp(moveRightKey);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveRightKey);
|
||||
rightKeyDown = false;
|
||||
}
|
||||
|
||||
if (!leftKeyDown)
|
||||
{
|
||||
_simulator.KeyDown(moveLeftKey);
|
||||
Simulation.SendInput.Keyboard.KeyDown(moveLeftKey);
|
||||
leftKeyDown = true;
|
||||
}
|
||||
}
|
||||
@@ -629,13 +626,13 @@ public class AutoDomainTask : ISoloTask
|
||||
if (leftKeyDown)
|
||||
{
|
||||
// 先松开A键
|
||||
_simulator.KeyUp(moveLeftKey);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveLeftKey);
|
||||
leftKeyDown = false;
|
||||
}
|
||||
|
||||
if (!rightKeyDown)
|
||||
{
|
||||
_simulator.KeyDown(moveRightKey);
|
||||
Simulation.SendInput.Keyboard.KeyDown(moveRightKey);
|
||||
rightKeyDown = true;
|
||||
}
|
||||
}
|
||||
@@ -644,14 +641,14 @@ public class AutoDomainTask : ISoloTask
|
||||
// 树在中间 松开所有键
|
||||
if (rightKeyDown)
|
||||
{
|
||||
_simulator.KeyUp(moveRightKey);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveRightKey);
|
||||
prevKey = moveRightKey;
|
||||
rightKeyDown = false;
|
||||
}
|
||||
|
||||
if (leftKeyDown)
|
||||
{
|
||||
_simulator.KeyUp(moveLeftKey);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveLeftKey);
|
||||
prevKey = moveLeftKey;
|
||||
leftKeyDown = false;
|
||||
}
|
||||
@@ -664,7 +661,9 @@ public class AutoDomainTask : ISoloTask
|
||||
backwardsAndForwardsCount++;
|
||||
}
|
||||
|
||||
_simulator.KeyPress(moveLeftKey, 60);
|
||||
Simulation.SendInput.Keyboard.KeyDown(moveLeftKey);
|
||||
Sleep(60);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveLeftKey);
|
||||
prevKey = moveLeftKey;
|
||||
}
|
||||
else if (treeMiddleX > middleX)
|
||||
@@ -674,12 +673,16 @@ public class AutoDomainTask : ISoloTask
|
||||
backwardsAndForwardsCount++;
|
||||
}
|
||||
|
||||
_simulator.KeyPress(moveRightKey, 60);
|
||||
Simulation.SendInput.Keyboard.KeyDown(moveRightKey);
|
||||
Sleep(60);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveRightKey);
|
||||
prevKey = moveRightKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
_simulator.KeyPress(moveForwardKey, 60);
|
||||
Simulation.SendInput.Keyboard.KeyDown(moveForwardKey);
|
||||
Sleep(60);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveForwardKey);
|
||||
Sleep(500, _ct);
|
||||
treeCts.Cancel();
|
||||
break;
|
||||
@@ -695,13 +698,13 @@ public class AutoDomainTask : ISoloTask
|
||||
{
|
||||
if (leftKeyDown)
|
||||
{
|
||||
_simulator.KeyUp(moveLeftKey);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveLeftKey);
|
||||
leftKeyDown = false;
|
||||
}
|
||||
|
||||
if (!rightKeyDown)
|
||||
{
|
||||
_simulator.KeyDown(moveRightKey);
|
||||
Simulation.SendInput.Keyboard.KeyDown(moveRightKey);
|
||||
rightKeyDown = true;
|
||||
}
|
||||
}
|
||||
@@ -709,13 +712,13 @@ public class AutoDomainTask : ISoloTask
|
||||
{
|
||||
if (rightKeyDown)
|
||||
{
|
||||
_simulator.KeyUp(moveRightKey);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveRightKey);
|
||||
rightKeyDown = false;
|
||||
}
|
||||
|
||||
if (!leftKeyDown)
|
||||
{
|
||||
_simulator.KeyDown(moveLeftKey);
|
||||
Simulation.SendInput.Keyboard.KeyDown(moveLeftKey);
|
||||
leftKeyDown = true;
|
||||
}
|
||||
}
|
||||
@@ -724,7 +727,9 @@ public class AutoDomainTask : ISoloTask
|
||||
if (backwardsAndForwardsCount >= _config.LeftRightMoveTimes)
|
||||
{
|
||||
// 左右移动5次说明已经在树中心了
|
||||
_simulator.KeyPress(moveForwardKey, 60);
|
||||
Simulation.SendInput.Keyboard.KeyDown(moveForwardKey);
|
||||
Sleep(60);
|
||||
Simulation.SendInput.Keyboard.KeyUp(moveForwardKey);
|
||||
Sleep(500, _ct);
|
||||
treeCts.Cancel();
|
||||
break;
|
||||
@@ -843,7 +848,7 @@ public class AutoDomainTask : ISoloTask
|
||||
break;
|
||||
}
|
||||
|
||||
var useCondensedResinRa = CaptureToRectArea().Find(AutoFightContext.Instance.FightAssets.UseCondensedResinRa);
|
||||
var useCondensedResinRa = CaptureToRectArea().Find(AutoFightAssets.Instance.UseCondensedResinRa);
|
||||
if (!useCondensedResinRa.IsEmpty())
|
||||
{
|
||||
useCondensedResinRa.Click();
|
||||
@@ -883,13 +888,13 @@ public class AutoDomainTask : ISoloTask
|
||||
|
||||
|
||||
// 优先点击继续
|
||||
using var confirmRectArea = ra.Find(AutoFightContext.Instance.FightAssets.ConfirmRa);
|
||||
using var confirmRectArea = ra.Find(AutoFightAssets.Instance.ConfirmRa);
|
||||
if (!confirmRectArea.IsEmpty())
|
||||
{
|
||||
if (isLastTurn)
|
||||
{
|
||||
// 最后一回合 退出
|
||||
var exitRectArea = ra.Find(AutoFightContext.Instance.FightAssets.ExitRa);
|
||||
var exitRectArea = ra.Find(AutoFightAssets.Instance.ExitRa);
|
||||
if (!exitRectArea.IsEmpty())
|
||||
{
|
||||
exitRectArea.Click();
|
||||
@@ -907,7 +912,7 @@ public class AutoDomainTask : ISoloTask
|
||||
if (condensedResinCount == 0 && fragileResinCount < 20)
|
||||
{
|
||||
// 没有体力了退出
|
||||
var exitRectArea = ra.Find(AutoFightContext.Instance.FightAssets.ExitRa);
|
||||
var exitRectArea = ra.Find(AutoFightAssets.Instance.ExitRa);
|
||||
if (!exitRectArea.IsEmpty())
|
||||
{
|
||||
exitRectArea.Click();
|
||||
@@ -938,7 +943,7 @@ public class AutoDomainTask : ISoloTask
|
||||
|
||||
var ra = CaptureToRectArea();
|
||||
// 浓缩树脂
|
||||
var condensedResinCountRa = ra.Find(AutoFightContext.Instance.FightAssets.CondensedResinCountRa);
|
||||
var condensedResinCountRa = ra.Find(AutoFightAssets.Instance.CondensedResinCountRa);
|
||||
if (!condensedResinCountRa.IsEmpty())
|
||||
{
|
||||
// 图像右侧就是浓缩树脂数量
|
||||
@@ -949,7 +954,7 @@ public class AutoDomainTask : ISoloTask
|
||||
}
|
||||
|
||||
// 脆弱树脂
|
||||
var fragileResinCountRa = ra.Find(AutoFightContext.Instance.FightAssets.FragileResinCountRa);
|
||||
var fragileResinCountRa = ra.Find(AutoFightAssets.Instance.FragileResinCountRa);
|
||||
if (!fragileResinCountRa.IsEmpty())
|
||||
{
|
||||
// 图像右侧就是脆弱树脂数量
|
||||
|
||||
@@ -12,6 +12,7 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
|
||||
public List<Rect> AvatarSideIconRectList; // 侧边栏角色头像 非联机状态下
|
||||
public List<Rect> AvatarIndexRectList; // 侧边栏角色头像对应的白色块 非联机状态下
|
||||
public Rect ERect;
|
||||
public Rect ECooldownRect;
|
||||
public Rect QRect;
|
||||
public Rect EndTipsUpperRect; // 挑战达成提示
|
||||
public Rect EndTipsRect;
|
||||
@@ -47,6 +48,8 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
|
||||
(int)(355 * AssetScale), (int)(465 * AssetScale));
|
||||
ERect = new Rect(CaptureRect.Width - (int)(267 * AssetScale), CaptureRect.Height - (int)(132 * AssetScale),
|
||||
(int)(77 * AssetScale), (int)(77 * AssetScale));
|
||||
ECooldownRect = new Rect(CaptureRect.Width - (int)(241 * AssetScale), CaptureRect.Height - (int)(97 * AssetScale),
|
||||
(int)(41 * AssetScale), (int)(18 * AssetScale));
|
||||
QRect = new Rect(CaptureRect.Width - (int)(157 * AssetScale), CaptureRect.Height - (int)(165 * AssetScale),
|
||||
(int)(110 * AssetScale), (int)(110 * AssetScale));
|
||||
// 小道具位置 1920-133,800,60,50
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
using BetterGenshinImpact.Core.Simulator;
|
||||
using BetterGenshinImpact.GameTask.AutoFight.Assets;
|
||||
using BetterGenshinImpact.Model;
|
||||
// using BetterGenshinImpact.Core.Simulator;
|
||||
// using BetterGenshinImpact.GameTask.AutoFight.Assets;
|
||||
// using BetterGenshinImpact.Model;
|
||||
//
|
||||
// namespace BetterGenshinImpact.GameTask.AutoFight;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoFight;
|
||||
|
||||
/// <summary>
|
||||
/// 自动战斗上下文
|
||||
/// 请在启动BetterGI以后再初始化
|
||||
/// </summary>
|
||||
public class AutoFightContext : Singleton<AutoFightContext>
|
||||
{
|
||||
private AutoFightContext()
|
||||
{
|
||||
Simulator = TaskContext.Instance().PostMessageSimulator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// find资源
|
||||
/// </summary>
|
||||
public AutoFightAssets FightAssets => AutoFightAssets.Instance;
|
||||
|
||||
/// <summary>
|
||||
/// 战斗专用的PostMessage模拟键鼠操作
|
||||
/// </summary>
|
||||
public readonly PostMessageSimulator Simulator;
|
||||
}
|
||||
// /// <summary>
|
||||
// /// 自动战斗上下文
|
||||
// /// 请在启动BetterGI以后再初始化
|
||||
// /// </summary>
|
||||
// public class AutoFightContext : Singleton<AutoFightContext>
|
||||
// {
|
||||
// private AutoFightContext()
|
||||
// {
|
||||
// Simulator = TaskContext.Instance().PostMessageSimulator;
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// find资源
|
||||
// /// </summary>
|
||||
// public AutoFightAssets FightAssets => AutoFightAssets.Instance;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 战斗专用的PostMessage模拟键鼠操作
|
||||
// /// </summary>
|
||||
// public readonly PostMessageSimulator Simulator;
|
||||
// }
|
||||
|
||||
@@ -248,7 +248,11 @@ public class AutoFightTask : ISoloTask
|
||||
double skillCd;
|
||||
if (lastFightName != command.Name && actionSchedulerByCd.TryGetValue(command.Name,out skillCd))
|
||||
{
|
||||
var avatar = combatScenes.Avatars.FirstOrDefault(a => a.Name == command.Name);
|
||||
var avatar = combatScenes.SelectAvatar(command.Name);
|
||||
if (avatar == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (skillCd < 0)
|
||||
{
|
||||
skillCd = FindMax([avatar.SkillCd,avatar.SkillHoldCd]);
|
||||
@@ -453,7 +457,7 @@ public class AutoFightTask : ISoloTask
|
||||
await Delay(450, _ct);
|
||||
var ra = CaptureToRectArea();
|
||||
var b3 = ra.SrcMat.At<Vec3b>(50, 790); //进度条颜色
|
||||
var whiteTile = ra.SrcMat.At<Vec3b>(50, 772); //白块
|
||||
var whiteTile = ra.SrcMat.At<Vec3b>(50, 768); //白块
|
||||
if (IsWhite(whiteTile.Item2, whiteTile.Item1, whiteTile.Item0) && IsYellow(b3.Item2, b3.Item1, b3.Item0) /* AreDifferencesWithinBounds(_finishDetectConfig.BattleEndProgressBarColor, (b3.Item0, b3.Item1, b3.Item2), _finishDetectConfig.BattleEndProgressBarColorTolerance)*/)
|
||||
{
|
||||
Logger.LogInformation("识别到战斗结束");
|
||||
@@ -462,7 +466,8 @@ public class AutoFightTask : ISoloTask
|
||||
}
|
||||
|
||||
Simulation.SendInput.SimulateAction(GIActions.Drop);
|
||||
Logger.LogInformation($"未识别到战斗结束{b3.Item0},{b3.Item1},{b3.Item2}");
|
||||
Logger.LogInformation($"未识别到战斗结束yellow{b3.Item0},{b3.Item1},{b3.Item2}");
|
||||
Logger.LogInformation($"未识别到战斗结束white{whiteTile.Item0},{whiteTile.Item1},{whiteTile.Item2}");
|
||||
/**
|
||||
if (!Bv.IsInMainUi(ra))
|
||||
{
|
||||
@@ -473,10 +478,6 @@ public class AutoFightTask : ISoloTask
|
||||
|
||||
_lastFightFlagTime = DateTime.Now;
|
||||
return false;
|
||||
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsYellow(int r, int g, int b)
|
||||
|
||||
@@ -18,6 +18,7 @@ using BetterGenshinImpact.GameTask.Common.BgiVision;
|
||||
using Vanara.PInvoke;
|
||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using BetterGenshinImpact.GameTask.AutoFight.Assets;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoFight.Model;
|
||||
|
||||
@@ -55,7 +56,7 @@ public class Avatar
|
||||
/// 长按元素战技CD
|
||||
/// </summary>
|
||||
public double SkillHoldCd { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 最近一次使用元素战技的时间
|
||||
/// </summary>
|
||||
@@ -119,7 +120,7 @@ public class Avatar
|
||||
{
|
||||
Logger.LogWarning("检测到复苏界面,存在角色被击败,前往七天神像复活");
|
||||
// 先打开地图
|
||||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_ESCAPE); // NOTE: 此处按下Esc是为了关闭复苏界面,无需改键
|
||||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_ESCAPE); // NOTE: 此处按下Esc是为了关闭复苏界面,无需改键
|
||||
Sleep(600, Ct);
|
||||
Simulation.SendInput.SimulateAction(GIActions.OpenMap);
|
||||
// tp 到七天神像复活
|
||||
@@ -155,23 +156,24 @@ public class Avatar
|
||||
switch (Index)
|
||||
{
|
||||
case 1:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember1);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember1);
|
||||
break;
|
||||
case 2:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember2);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember2);
|
||||
break;
|
||||
case 3:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember3);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember3);
|
||||
break;
|
||||
case 4:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember4);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember4);
|
||||
break;
|
||||
case 5:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember5);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember5);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Debug.WriteLine($"切换到{Index}号位");
|
||||
// Cv2.ImWrite($"log/切换.png", region.SrcMat);
|
||||
Sleep(250, Ct);
|
||||
@@ -210,19 +212,19 @@ public class Avatar
|
||||
switch (Index)
|
||||
{
|
||||
case 1:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember1);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember1);
|
||||
break;
|
||||
case 2:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember2);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember2);
|
||||
break;
|
||||
case 3:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember3);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember3);
|
||||
break;
|
||||
case 4:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember4);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember4);
|
||||
break;
|
||||
case 5:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember5);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember5);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -254,23 +256,24 @@ public class Avatar
|
||||
switch (Index)
|
||||
{
|
||||
case 1:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember1);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember1);
|
||||
break;
|
||||
case 2:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember2);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember2);
|
||||
break;
|
||||
case 3:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember3);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember3);
|
||||
break;
|
||||
case 4:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember4);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember4);
|
||||
break;
|
||||
case 5:
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SwitchMember5);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SwitchMember5);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Sleep(250);
|
||||
}
|
||||
}
|
||||
@@ -314,7 +317,7 @@ public class Avatar
|
||||
{
|
||||
var assetScale = TaskContext.Instance().SystemInfo.AssetScale;
|
||||
// 剪裁出队伍区域
|
||||
var teamRa = region.DeriveCrop(AutoFightContext.Instance.FightAssets.TeamRect);
|
||||
var teamRa = region.DeriveCrop(AutoFightAssets.Instance.TeamRect);
|
||||
var blockX = NameRect.X + NameRect.Width * 2 - 10;
|
||||
var block = teamRa.DeriveCrop(new Rect(blockX, NameRect.Y, teamRa.Width - blockX, NameRect.Height * 2));
|
||||
// Cv2.ImWrite($"block_{Name}.png", block.SrcMat);
|
||||
@@ -337,7 +340,7 @@ public class Avatar
|
||||
else
|
||||
{
|
||||
// 剪裁出IndexRect区域
|
||||
var teamRa = region.DeriveCrop(AutoFightContext.Instance.FightAssets.TeamRect);
|
||||
var teamRa = region.DeriveCrop(AutoFightAssets.Instance.TeamRect);
|
||||
var blockX = NameRect.X + NameRect.Width * 2 - 10;
|
||||
var indexBlock = teamRa.DeriveCrop(new Rect(blockX + IndexRect.X, NameRect.Y + IndexRect.Y, IndexRect.Width, IndexRect.Height));
|
||||
// Cv2.ImWrite($"indexBlock_{Name}.png", indexBlock.SrcMat);
|
||||
@@ -365,7 +368,7 @@ public class Avatar
|
||||
return;
|
||||
}
|
||||
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack);
|
||||
Simulation.SendInput.SimulateAction(GIActions.NormalAttack);
|
||||
ms -= 200;
|
||||
Sleep(200, Ct);
|
||||
}
|
||||
@@ -387,7 +390,7 @@ public class Avatar
|
||||
{
|
||||
if (Name == "纳西妲")
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyDown);
|
||||
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.KeyDown);
|
||||
Sleep(300, Ct);
|
||||
for (int j = 0; j < 10; j++)
|
||||
{
|
||||
@@ -396,22 +399,22 @@ public class Avatar
|
||||
}
|
||||
|
||||
Sleep(300); // 持续操作不应该被cts取消
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyUp);
|
||||
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.KeyUp);
|
||||
}
|
||||
else if (Name == "坎蒂丝")
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyDown);
|
||||
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.KeyDown);
|
||||
Thread.Sleep(3000);
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyUp);
|
||||
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.KeyUp);
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.Hold);
|
||||
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.Hold);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalSkill, KeyType.KeyPress);
|
||||
Simulation.SendInput.SimulateAction(GIActions.ElementalSkill, KeyType.KeyPress);
|
||||
}
|
||||
|
||||
Sleep(200, Ct);
|
||||
@@ -436,8 +439,9 @@ public class Avatar
|
||||
/// </summary>
|
||||
public double GetSkillCurrentCd(ImageRegion imageRegion)
|
||||
{
|
||||
var eRa = imageRegion.DeriveCrop(AutoFightContext.Instance.FightAssets.ERect);
|
||||
var text = OcrFactory.Paddle.Ocr(eRa.SrcGreyMat);
|
||||
var eRa = imageRegion.DeriveCrop(AutoFightAssets.Instance.ECooldownRect);
|
||||
var eRaWhite = OpenCvCommonHelper.InRangeHsv(eRa.SrcMat, new Scalar(0, 0, 235), new Scalar(0, 25, 255));
|
||||
var text = OcrFactory.Paddle.OcrWithoutDetector(eRaWhite);
|
||||
return StringUtils.TryParseDouble(text);
|
||||
}
|
||||
|
||||
@@ -455,7 +459,7 @@ public class Avatar
|
||||
return;
|
||||
}
|
||||
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.ElementalBurst);
|
||||
Simulation.SendInput.SimulateAction(GIActions.ElementalBurst);
|
||||
Sleep(200, Ct);
|
||||
|
||||
var region = CaptureToRectArea();
|
||||
@@ -490,7 +494,7 @@ public class Avatar
|
||||
// /// </summary>
|
||||
// public double GetBurstCurrentCd(CaptureContent content)
|
||||
// {
|
||||
// var qRa = content.CaptureRectArea.Crop(AutoFightContext.Instance.FightAssets.QRect);
|
||||
// var qRa = content.CaptureRectArea.Crop(AutoFightAssets.Instance.QRect);
|
||||
// var text = OcrFactory.Paddle.Ocr(qRa.SrcGreyMat);
|
||||
// return StringUtils.TryParseDouble(text);
|
||||
// }
|
||||
@@ -510,9 +514,9 @@ public class Avatar
|
||||
ms = 200;
|
||||
}
|
||||
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SprintMouse, KeyType.KeyDown);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SprintMouse, KeyType.KeyDown);
|
||||
Sleep(ms); // 冲刺不能被cts取消
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.SprintMouse, KeyType.KeyUp);
|
||||
Simulation.SendInput.SimulateAction(GIActions.SprintMouse, KeyType.KeyUp);
|
||||
}
|
||||
|
||||
public void Walk(string key, int ms)
|
||||
@@ -545,9 +549,9 @@ public class Avatar
|
||||
return;
|
||||
}
|
||||
|
||||
AutoFightContext.Instance.Simulator.KeyDown(vk);
|
||||
Simulation.SendInput.Keyboard.KeyDown(vk);
|
||||
Sleep(ms); // 行走不能被cts取消
|
||||
AutoFightContext.Instance.Simulator.KeyUp(vk);
|
||||
Simulation.SendInput.Keyboard.KeyUp(vk);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -574,7 +578,7 @@ public class Avatar
|
||||
/// </summary>
|
||||
public void Jump()
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.Jump);
|
||||
Simulation.SendInput.SimulateAction(GIActions.Jump);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -590,7 +594,7 @@ public class Avatar
|
||||
if (Name == "那维莱特")
|
||||
{
|
||||
var dpi = TaskContext.Instance().DpiScale;
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack, KeyType.KeyDown);
|
||||
Simulation.SendInput.SimulateAction(GIActions.NormalAttack, KeyType.KeyDown);
|
||||
while (ms >= 0)
|
||||
{
|
||||
if (Ct is { IsCancellationRequested: true })
|
||||
@@ -603,13 +607,13 @@ public class Avatar
|
||||
Sleep(50); // 持续操作不应该被cts取消
|
||||
}
|
||||
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack, KeyType.KeyUp);
|
||||
Simulation.SendInput.SimulateAction(GIActions.NormalAttack, KeyType.KeyUp);
|
||||
}
|
||||
else
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack, KeyType.KeyDown);
|
||||
Simulation.SendInput.SimulateAction(GIActions.NormalAttack, KeyType.KeyDown);
|
||||
Sleep(ms); // 持续操作不应该被cts取消
|
||||
AutoFightContext.Instance.Simulator.SimulateAction(GIActions.NormalAttack, KeyType.KeyUp);
|
||||
Simulation.SendInput.SimulateAction(GIActions.NormalAttack, KeyType.KeyUp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,11 +622,11 @@ public class Avatar
|
||||
key = key.ToLower();
|
||||
if (key == "left")
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.LeftButtonDown();
|
||||
Simulation.SendInput.Mouse.LeftButtonDown();
|
||||
}
|
||||
else if (key == "right")
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.RightButtonDown();
|
||||
Simulation.SendInput.Mouse.RightButtonDown();
|
||||
}
|
||||
else if (key == "middle")
|
||||
{
|
||||
@@ -635,11 +639,11 @@ public class Avatar
|
||||
key = key.ToLower();
|
||||
if (key == "left")
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.LeftButtonUp();
|
||||
Simulation.SendInput.Mouse.LeftButtonUp();
|
||||
}
|
||||
else if (key == "right")
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.RightButtonUp();
|
||||
Simulation.SendInput.Mouse.RightButtonUp();
|
||||
}
|
||||
else if (key == "middle")
|
||||
{
|
||||
@@ -652,11 +656,11 @@ public class Avatar
|
||||
key = key.ToLower();
|
||||
if (key == "left")
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.LeftButtonClick();
|
||||
Simulation.SendInput.Mouse.LeftButtonClick();
|
||||
}
|
||||
else if (key == "right")
|
||||
{
|
||||
AutoFightContext.Instance.Simulator.RightButtonClick();
|
||||
Simulation.SendInput.Mouse.RightButtonClick();
|
||||
}
|
||||
else if (key == "middle")
|
||||
{
|
||||
@@ -672,18 +676,18 @@ public class Avatar
|
||||
public void KeyDown(string key)
|
||||
{
|
||||
var vk = User32Helper.ToVk(key);
|
||||
AutoFightContext.Instance.Simulator.KeyDown(vk);
|
||||
Simulation.SendInput.Keyboard.KeyDown(vk);
|
||||
}
|
||||
|
||||
public void KeyUp(string key)
|
||||
{
|
||||
var vk = User32Helper.ToVk(key);
|
||||
AutoFightContext.Instance.Simulator.KeyUp(vk);
|
||||
Simulation.SendInput.Keyboard.KeyUp(vk);
|
||||
}
|
||||
|
||||
public void KeyPress(string key)
|
||||
{
|
||||
var vk = User32Helper.ToVk(key);
|
||||
AutoFightContext.Instance.Simulator.KeyPress(vk);
|
||||
Simulation.SendInput.Keyboard.KeyPress(vk);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using BetterGenshinImpact.Core.Simulator;
|
||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoFight.Model;
|
||||
@@ -168,7 +169,7 @@ public class CombatScenes : IDisposable
|
||||
if (result.TopClass.Confidence < 0.51)
|
||||
{
|
||||
Cv2.ImWrite(@"log\avatar_side_classify_error.png", src.ToMat());
|
||||
throw new Exception($"无法识别第{index}位角色,置信度{result.TopClass.Confidence:F1},结果:{result.TopClass.Name.Name}。请重新阅读了文档中的《快速上手》!");
|
||||
throw new Exception($"无法识别第{index}位角色,置信度{result.TopClass.Confidence:F1},结果:{result.TopClass.Name.Name}。请重新阅读 BetterGI 文档中的《快速上手》!");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -176,7 +177,7 @@ public class CombatScenes : IDisposable
|
||||
if (result.TopClass.Confidence < 0.7)
|
||||
{
|
||||
Cv2.ImWrite(@"log\avatar_side_classify_error.png", src.ToMat());
|
||||
throw new Exception($"无法识别第{index}位角色,置信度{result.TopClass.Confidence:F1},结果:{result.TopClass.Name.Name}。请重新阅读了文档中的《快速上手》!");
|
||||
throw new Exception($"无法识别第{index}位角色,置信度{result.TopClass.Confidence:F1},结果:{result.TopClass.Name.Name}。请重新阅读 BetterGI 文档中的《快速上手》!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +218,7 @@ public class CombatScenes : IDisposable
|
||||
{
|
||||
if (avatarIndexRectList == null && ExpectedTeamAvatarNum == 4)
|
||||
{
|
||||
avatarIndexRectList = AutoFightContext.Instance.FightAssets.AvatarIndexRectList;
|
||||
avatarIndexRectList = AutoFightAssets.Instance.AvatarIndexRectList;
|
||||
}
|
||||
|
||||
if (avatarIndexRectList == null)
|
||||
@@ -249,6 +250,9 @@ public class CombatScenes : IDisposable
|
||||
|
||||
public void AfterTask()
|
||||
{
|
||||
// 释放所有按键
|
||||
Simulation.ReleaseAllKey();
|
||||
|
||||
var mwk = SelectAvatar("玛薇卡");
|
||||
if (mwk != null)
|
||||
{
|
||||
@@ -284,7 +288,7 @@ public class CombatScenes : IDisposable
|
||||
}
|
||||
|
||||
// 剪裁出队伍区域
|
||||
var teamRa = content.CaptureRectArea.DeriveCrop(AutoFightContext.Instance.FightAssets.TeamRectNoIndex);
|
||||
var teamRa = content.CaptureRectArea.DeriveCrop(AutoFightAssets.Instance.TeamRectNoIndex);
|
||||
// 过滤出白色
|
||||
var hsvFilterMat = OpenCvCommonHelper.InRangeHsv(teamRa.SrcMat, new Scalar(0, 0, 210), new Scalar(255, 30, 255));
|
||||
|
||||
@@ -319,10 +323,10 @@ public class CombatScenes : IDisposable
|
||||
{
|
||||
// 流浪者特殊处理
|
||||
// 4人以上的队伍,不支持流浪者的识别
|
||||
var wanderer = rectArea.Find(AutoFightContext.Instance.FightAssets.WandererIconRa);
|
||||
var wanderer = rectArea.Find(AutoFightAssets.Instance.WandererIconRa);
|
||||
if (wanderer.IsEmpty())
|
||||
{
|
||||
wanderer = rectArea.Find(AutoFightContext.Instance.FightAssets.WandererIconNoActiveRa);
|
||||
wanderer = rectArea.Find(AutoFightAssets.Instance.WandererIconNoActiveRa);
|
||||
}
|
||||
|
||||
if (wanderer.IsEmpty())
|
||||
|
||||
@@ -67,35 +67,34 @@ public class CombatCommand
|
||||
|
||||
public void Execute(CombatScenes combatScenes)
|
||||
{
|
||||
// 如果是当前角色
|
||||
Avatar? avatar;
|
||||
if (Name == CombatScriptParser.CurrentAvatarName)
|
||||
{
|
||||
// 如果是当前角色,不进行角色切换
|
||||
avatar = combatScenes.Avatars[0]; // 随便取一个角色
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其余情况要进行角色切换
|
||||
avatar = combatScenes.SelectAvatar(Name);
|
||||
if (avatar == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 非宏类脚本,等待切换角色成功
|
||||
if (Method != Method.Wait
|
||||
&& Method != Method.MouseDown
|
||||
&& Method != Method.MouseUp
|
||||
&& Method != Method.Click
|
||||
&& Method != Method.MoveBy
|
||||
&& Method != Method.KeyDown
|
||||
&& Method != Method.KeyUp
|
||||
&& Method != Method.KeyPress)
|
||||
{
|
||||
avatar.Switch();
|
||||
}
|
||||
}
|
||||
if (avatar == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 非宏类脚本,等待切换角色成功
|
||||
if (Method != Method.Wait
|
||||
&& Method != Method.MouseDown
|
||||
&& Method != Method.MouseUp
|
||||
&& Method != Method.Click
|
||||
&& Method != Method.MoveBy
|
||||
&& Method != Method.KeyDown
|
||||
&& Method != Method.KeyUp
|
||||
&& Method != Method.KeyPress)
|
||||
{
|
||||
avatar.Switch();
|
||||
}
|
||||
|
||||
|
||||
Execute(avatar);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoGeniusInvokation.Model;
|
||||
|
||||
@@ -60,9 +61,10 @@ public class Duel
|
||||
LogScreenResolution();
|
||||
try
|
||||
{
|
||||
Notify.Event(NotificationEvent.TcgStart).Success("自动七胜召唤启动");
|
||||
|
||||
AutoGeniusInvokationAssets.DestroyInstance();
|
||||
|
||||
NotificationHelper.SendTaskNotificationUsing(b => b.GeniusInvocation().Started().Build()); // TODO 需要移动
|
||||
|
||||
GeniusInvokationControl.GetInstance().Init(ct);
|
||||
|
||||
// 对局准备 选择初始手牌
|
||||
@@ -288,14 +290,12 @@ public class Duel
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
NotificationHelper.SendTaskNotificationUsing(b => b.GeniusInvocation().Cancelled().Build());
|
||||
throw;
|
||||
}
|
||||
catch (NormalEndException ex)
|
||||
{
|
||||
_logger.LogInformation("对局结束");
|
||||
NotificationHelper.SendTaskNotificationUsing(b => b.GeniusInvocation().Success().Build());
|
||||
throw;
|
||||
// throw;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
@@ -303,9 +303,10 @@ public class Duel
|
||||
{
|
||||
_logger.LogError(ex.StackTrace);
|
||||
}
|
||||
NotificationHelper.SendTaskNotificationUsing(b => b.GeniusInvocation().Failure().Build());
|
||||
throw;
|
||||
}
|
||||
|
||||
Notify.Event(NotificationEvent.TcgEnd).Success("自动七胜召唤结束");
|
||||
}
|
||||
|
||||
private HashSet<ElementalType> PredictionDiceType()
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Core.Recognition;
|
||||
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
|
||||
using BetterGenshinImpact.GameTask.AutoMusicGame.Assets;
|
||||
using BetterGenshinImpact.GameTask.Common.BgiVision;
|
||||
using BetterGenshinImpact.GameTask.Model.Area;
|
||||
using BetterGenshinImpact.Service.Notification;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||
|
||||
@@ -24,21 +29,38 @@ public class AutoAlbumTask(AutoMusicGameParam taskParam) : ISoloTask
|
||||
try
|
||||
{
|
||||
AutoMusicGameTask.Init();
|
||||
Notify.Event(NotificationEvent.AlbumStart).Success("自动音游专辑启动");
|
||||
Logger.LogInformation("开始自动演奏整个专辑未完成的音乐");
|
||||
await StartOneAlbum(ct);
|
||||
Notify.Event(NotificationEvent.AlbumEnd).Success("自动音游专辑结束");
|
||||
}
|
||||
catch (NormalEndException e)
|
||||
{
|
||||
Logger.LogError("手动取消任务 - {Msg}", e.Message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError("自动音乐专辑任务异常:{Msg}", e.Message);
|
||||
Notify.Event(NotificationEvent.AlbumError).Error("自动音游专辑异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartOneAlbum(CancellationToken ct)
|
||||
{
|
||||
using var iconRa = CaptureToRectArea().Find(AutoMusicAssets.Instance.UiLeftTopAlbumIcon);
|
||||
using var ra1 = CaptureToRectArea();
|
||||
using var iconRa = ra1.Find(AutoMusicAssets.Instance.UiLeftTopAlbumIcon);
|
||||
if (!iconRa.IsExist())
|
||||
{
|
||||
throw new Exception("当前未处于专辑界面,请在专辑界面运行本任务");
|
||||
throw new Exception("当前未处于主题专辑界面,请在专辑界面运行本任务。注意全部歌曲列表页面无法运行本任务!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// OCR 后再次判断,区分是否是全部歌曲页面
|
||||
var ocrRes = ra1.DeriveCrop(iconRa.Right, iconRa.Top, ra1.Width * 0.16, iconRa.Height).FindMulti(RecognitionObject.OcrThis);
|
||||
if (ocrRes.Any(region => region.Text.Contains("全部")))
|
||||
{
|
||||
throw new Exception("当前在全部歌曲页面,此页面无法运行本任务。请返回到主界面选择专辑列表中以国家为主题的专辑页!");
|
||||
}
|
||||
}
|
||||
|
||||
var musicLevel = TaskContext.Instance().Config.AutoMusicGameConfig.MusicLevel;
|
||||
@@ -46,13 +68,14 @@ public class AutoAlbumTask(AutoMusicGameParam taskParam) : ISoloTask
|
||||
{
|
||||
musicLevel = "传说";
|
||||
}
|
||||
|
||||
Logger.LogInformation("自动音游乐曲难度等级:{Text}", musicLevel);
|
||||
|
||||
|
||||
// 遍历4个难度等级
|
||||
var defaultDifficultyLevels = new[]
|
||||
{
|
||||
("普通", 480, 600, AutoMusicAssets.Instance.MusicCanorusLevel1),
|
||||
("困难", 800, 600, AutoMusicAssets.Instance.MusicCanorusLevel2),
|
||||
("困难", 800, 600, AutoMusicAssets.Instance.MusicCanorusLevel2),
|
||||
("大师", 1150, 600, AutoMusicAssets.Instance.MusicCanorusLevel3),
|
||||
("传说", 1400, 600, AutoMusicAssets.Instance.MusicCanorusLevel4)
|
||||
};
|
||||
@@ -66,34 +89,51 @@ public class AutoAlbumTask(AutoMusicGameParam taskParam) : ISoloTask
|
||||
foreach (var (difficultyName, xPos, yPos, canorusAsset) in difficultyLevels)
|
||||
{
|
||||
Logger.LogInformation("开始演奏{Difficulty}难度的乐曲", difficultyName);
|
||||
|
||||
|
||||
// 每个难度12首曲子
|
||||
for (int i = 0; i < 13; i++)
|
||||
{
|
||||
using var canoraRa = CaptureToRectArea().Find(canorusAsset);
|
||||
if (canoraRa.IsExist())
|
||||
if (TaskContext.Instance().Config.AutoMusicGameConfig.MustCanorusLevel)
|
||||
{
|
||||
Logger.LogInformation("乐曲{Num} - {Difficulty}级别:已完成【大音天籁】,切换下一首", i + 1, difficultyName);
|
||||
GameCaptureRegion.GameRegion1080PPosClick(310, 220);
|
||||
await Delay(800, ct);
|
||||
continue;
|
||||
using var canoraRa = CaptureToRectArea().Find(canorusAsset);
|
||||
if (canoraRa.IsExist())
|
||||
{
|
||||
Logger.LogInformation("乐曲{Num} - {Difficulty}级别:已完成【大音天籁】,切换下一首", i + 1, difficultyName);
|
||||
GameCaptureRegion.GameRegion1080PPosClick(310, 220);
|
||||
await Delay(800, ct);
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.LogInformation("第{Num}首{Difficulty}难度的乐曲:{Message}", i + 1, difficultyName, "没有完成【大音天籁】");
|
||||
}
|
||||
else
|
||||
{
|
||||
using var doneRa = CaptureToRectArea().Find(AutoMusicAssets.Instance.AlbumMusicComplate);
|
||||
if (doneRa.IsExist())
|
||||
{
|
||||
Logger.LogInformation("当前乐曲{Num}所有奖励已领取,切换下一首", i + 1);
|
||||
GameCaptureRegion.GameRegion1080PPosClick(310, 220);
|
||||
await Delay(800, ct);
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.LogInformation("当前乐曲{Num}存在未领取奖励,前往演奏", i + 1);
|
||||
}
|
||||
|
||||
Logger.LogInformation("第{Num}首{Difficulty}难度的乐曲:{Message}", i + 1, difficultyName, "没有完成【大音天籁】");
|
||||
|
||||
|
||||
// 点击确认按钮
|
||||
Bv.ClickWhiteConfirmButton(CaptureToRectArea());
|
||||
await Delay(800, ct);
|
||||
|
||||
|
||||
// 选择难度
|
||||
GameCaptureRegion.GameRegion1080PPosClick(xPos, yPos);
|
||||
await Delay(200, ct);
|
||||
|
||||
|
||||
// 开始演奏
|
||||
Bv.ClickWhiteConfirmButton(CaptureToRectArea());
|
||||
await Delay(500, ct);
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
var cts = new CancellationTokenSource();
|
||||
ct.Register(cts.Cancel);
|
||||
|
||||
// 演奏结束检查任务
|
||||
@@ -118,16 +158,16 @@ public class AutoAlbumTask(AutoMusicGameParam taskParam) : ISoloTask
|
||||
// 等待任意一个任务完成
|
||||
await Task.WhenAny(checkTask, musicTask);
|
||||
await cts.CancelAsync();
|
||||
|
||||
|
||||
Logger.LogInformation("第{Num}首{Difficulty}难度乐曲演奏完成", i + 1, difficultyName);
|
||||
await Delay(2000, ct);
|
||||
|
||||
|
||||
await Bv.WaitUntilFound(AutoMusicAssets.Instance.UiLeftTopAlbumIcon, ct);
|
||||
Logger.LogDebug("切换到下一首乐曲");
|
||||
GameCaptureRegion.GameRegion1080PPosClick(310, 220);
|
||||
await Delay(800, ct);
|
||||
}
|
||||
|
||||
|
||||
Logger.LogInformation("完成{Difficulty}难度所有乐曲的演奏", difficultyName);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ namespace BetterGenshinImpact.GameTask.AutoMusicGame;
|
||||
[Serializable]
|
||||
public partial class AutoMusicGameConfig : ObservableObject
|
||||
{
|
||||
// 自动达到大音天籁的级别
|
||||
[ObservableProperty]
|
||||
private bool _mustCanorusLevel = false;
|
||||
|
||||
// 乐曲级别
|
||||
[ObservableProperty]
|
||||
private string _musicLevel = "";
|
||||
|
||||
@@ -112,6 +112,7 @@ public class ElementalCollectAvatarConfigs
|
||||
new ElementalCollectAvatar("鹿野院平藏", ElementalType.Anemo, true, true),
|
||||
new ElementalCollectAvatar("流浪者", ElementalType.Anemo, true, false),
|
||||
new ElementalCollectAvatar("闲云", ElementalType.Anemo, true, false),
|
||||
new ElementalCollectAvatar("蓝砚", ElementalType.Anemo, true, false),
|
||||
new ElementalCollectAvatar("枫原万叶", ElementalType.Anemo, false, true),
|
||||
new ElementalCollectAvatar("珐露珊", ElementalType.Anemo, false, true),
|
||||
new ElementalCollectAvatar("琳妮特", ElementalType.Anemo, false, true),
|
||||
|
||||
@@ -2,23 +2,23 @@
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoPathing.Model.Enum;
|
||||
|
||||
public class ActionEnum(string code, string msg)
|
||||
public class ActionEnum(string code, string msg, ActionUseWaypointTypeEnum useWaypointTypeEnum)
|
||||
{
|
||||
public static readonly ActionEnum StopFlying = new("stop_flying", "下落攻击");
|
||||
public static readonly ActionEnum ForceTp = new("force_tp", "当前点传送");
|
||||
public static readonly ActionEnum NahidaCollect = new("nahida_collect", "纳西妲长按E收集");
|
||||
public static readonly ActionEnum PickAround = new("pick_around", "尝试在周围拾取");
|
||||
public static readonly ActionEnum Fight = new("fight", "战斗");
|
||||
public static readonly ActionEnum UpDownGrabLeaf = new("up_down_grab_leaf", "四叶印");
|
||||
public static readonly ActionEnum StopFlying = new("stop_flying", "下落攻击", ActionUseWaypointTypeEnum.Custom);
|
||||
public static readonly ActionEnum ForceTp = new("force_tp", "当前点传送", ActionUseWaypointTypeEnum.Custom);
|
||||
public static readonly ActionEnum NahidaCollect = new("nahida_collect", "纳西妲长按E收集", ActionUseWaypointTypeEnum.Custom);
|
||||
public static readonly ActionEnum PickAround = new("pick_around", "尝试在周围拾取", ActionUseWaypointTypeEnum.Custom);
|
||||
public static readonly ActionEnum Fight = new("fight", "战斗", ActionUseWaypointTypeEnum.Path);
|
||||
public static readonly ActionEnum UpDownGrabLeaf = new("up_down_grab_leaf", "四叶印", ActionUseWaypointTypeEnum.Custom);
|
||||
|
||||
public static readonly ActionEnum HydroCollect = new("hydro_collect", "水元素力采集");
|
||||
public static readonly ActionEnum ElectroCollect = new("electro_collect", "雷元素力采集");
|
||||
public static readonly ActionEnum AnemoCollect = new("anemo_collect", "风元素力采集");
|
||||
|
||||
public static readonly ActionEnum CombatScript = new("combat_script", "战斗策略脚本"); // 这个必须要 action_params 里面有脚本
|
||||
|
||||
public static readonly ActionEnum Mining = new("mining", "挖矿");
|
||||
public static readonly ActionEnum LogOutput = new("log_output", "输出日志");
|
||||
public static readonly ActionEnum HydroCollect = new("hydro_collect", "水元素力采集", ActionUseWaypointTypeEnum.Target);
|
||||
public static readonly ActionEnum ElectroCollect = new("electro_collect", "雷元素力采集", ActionUseWaypointTypeEnum.Target);
|
||||
public static readonly ActionEnum AnemoCollect = new("anemo_collect", "风元素力采集", ActionUseWaypointTypeEnum.Target);
|
||||
|
||||
public static readonly ActionEnum CombatScript = new("combat_script", "战斗策略脚本", ActionUseWaypointTypeEnum.Custom); // 这个必须要 action_params 里面有脚本
|
||||
|
||||
public static readonly ActionEnum Mining = new("mining", "挖矿", ActionUseWaypointTypeEnum.Custom);
|
||||
public static readonly ActionEnum LogOutput = new("log_output", "输出日志", ActionUseWaypointTypeEnum.Custom);
|
||||
|
||||
// 还有要加入的其他动作
|
||||
// 滚轮F
|
||||
@@ -29,15 +29,32 @@ public class ActionEnum(string code, string msg)
|
||||
|
||||
public static IEnumerable<ActionEnum> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return StopFlying;
|
||||
}
|
||||
get { yield return StopFlying; }
|
||||
}
|
||||
|
||||
public string Code { get; private set; } = code;
|
||||
public string Msg { get; private set; } = msg;
|
||||
|
||||
public ActionUseWaypointTypeEnum UseWaypointTypeEnum { get; private set; } = useWaypointTypeEnum;
|
||||
|
||||
public static ActionEnum? GetEnumByCode(string? code)
|
||||
{
|
||||
if (string.IsNullOrEmpty(code))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var item in Values)
|
||||
{
|
||||
if (item.Code == code)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetMsgByCode(string code)
|
||||
{
|
||||
foreach (var item in Values)
|
||||
@@ -47,6 +64,14 @@ public class ActionEnum(string code, string msg)
|
||||
return item.Msg;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ActionUseWaypointTypeEnum
|
||||
{
|
||||
Custom, // 跟随路径点 Type
|
||||
Path, // 强制 Path
|
||||
Target // 强制 Target
|
||||
}
|
||||
@@ -7,6 +7,7 @@ public class WaypointType(string code, string msg)
|
||||
public static readonly WaypointType Path = new("path", "途径点");
|
||||
public static readonly WaypointType Target = new("target", "目标点");
|
||||
public static readonly WaypointType Teleport = new("teleport", "传送点");
|
||||
public static readonly WaypointType Orientation = new("orientation", "方位点");
|
||||
|
||||
public static IEnumerable<WaypointType> Values
|
||||
{
|
||||
@@ -15,6 +16,7 @@ public class WaypointType(string code, string msg)
|
||||
yield return Path;
|
||||
yield return Target;
|
||||
yield return Teleport;
|
||||
yield return Orientation;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ public class PathExecutor
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
InitializePathing(task);
|
||||
// 转换、按传送点分割路径
|
||||
var waypointsList = ConvertWaypointsForTrack(task.Positions);
|
||||
@@ -154,13 +154,13 @@ public class PathExecutor
|
||||
foreach (var waypoints in waypointsList) // 按传送点分割的路径
|
||||
{
|
||||
CurWaypoints = (waypointsList.FindIndex(wps => wps == waypoints), waypoints);
|
||||
|
||||
|
||||
for (var i = 0; i < RetryTimes; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ResolveAnomalies(); // 异常场景处理
|
||||
foreach (var waypoint in waypoints) // 一条路径
|
||||
foreach (var waypoint in waypoints) // 一条路径
|
||||
{
|
||||
CurWaypoint = (waypoints.FindIndex(wps => wps == waypoint), waypoint);
|
||||
TryCloseSkipOtherOperations();
|
||||
@@ -179,20 +179,22 @@ public class PathExecutor
|
||||
await BeforeMoveToTarget(waypoint);
|
||||
|
||||
// Path不用走得很近,Target需要接近,但都需要先移动到对应位置
|
||||
await MoveTo(waypoint);
|
||||
|
||||
if (waypoint.Type == WaypointType.Target.Code
|
||||
// 除了 fight mining stop_flying 之外的 action 都需要接近
|
||||
|| (!string.IsNullOrEmpty(waypoint.Action)
|
||||
&& waypoint.Action != ActionEnum.NahidaCollect.Code
|
||||
&& waypoint.Action != ActionEnum.Fight.Code
|
||||
&& waypoint.Action != ActionEnum.CombatScript.Code
|
||||
&& waypoint.Action != ActionEnum.Mining.Code))
|
||||
if (waypoint.Type == WaypointType.Orientation.Code)
|
||||
{
|
||||
if (waypoint.Action != ActionEnum.Fight.Code) // 战斗action强制不接近
|
||||
{
|
||||
await MoveCloseTo(waypoint);
|
||||
}
|
||||
// 方位点,只需要朝向
|
||||
// 考虑到方位点大概率是作为执行action的最后一个点,所以放在此处处理,不和传送点一样单独处理
|
||||
await FaceTo(waypoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MoveTo(waypoint);
|
||||
}
|
||||
|
||||
await BeforeMoveCloseToTarget(waypoint);
|
||||
|
||||
if (IsTargetPoint(waypoint))
|
||||
{
|
||||
await MoveCloseTo(waypoint);
|
||||
}
|
||||
|
||||
//skipOtherOperations如果重试,则跳过相关操作,
|
||||
@@ -251,6 +253,25 @@ public class PathExecutor
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsTargetPoint(WaypointForTrack waypoint)
|
||||
{
|
||||
// 方位点不需要接近
|
||||
if (waypoint.Type == WaypointType.Orientation.Code)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var action = ActionEnum.GetEnumByCode(waypoint.Action);
|
||||
if (action is not null && action.UseWaypointTypeEnum != ActionUseWaypointTypeEnum.Custom)
|
||||
{
|
||||
// 强制点位类型的 action,以 action 为准
|
||||
return action.UseWaypointTypeEnum == ActionUseWaypointTypeEnum.Target;
|
||||
}
|
||||
|
||||
// 其余情况和没有action的情况以点位类型为准
|
||||
return waypoint.Type == WaypointType.Target.Code;
|
||||
}
|
||||
|
||||
private async Task<bool> SwitchPartyBefore(PathingTask task)
|
||||
{
|
||||
var ra = CaptureToRectArea();
|
||||
@@ -564,6 +585,16 @@ public class PathExecutor
|
||||
await Delay(500, ct); // 多等一会
|
||||
}
|
||||
|
||||
private async Task FaceTo(WaypointForTrack waypoint)
|
||||
{
|
||||
var screen = CaptureToRectArea();
|
||||
var position = await GetPosition(screen);
|
||||
var targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
|
||||
Logger.LogInformation("朝向点,位置({x2},{y2})", $"{waypoint.GameX:F1}", $"{waypoint.GameY:F1}");
|
||||
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 2);
|
||||
await Delay(500, ct);
|
||||
}
|
||||
|
||||
private async Task MoveTo(WaypointForTrack waypoint)
|
||||
{
|
||||
// 切人
|
||||
@@ -806,20 +837,11 @@ public class PathExecutor
|
||||
|
||||
private async Task MoveCloseTo(WaypointForTrack waypoint)
|
||||
{
|
||||
var screen = CaptureToRectArea();
|
||||
var position = await GetPosition(screen);
|
||||
var targetOrientation = Navigation.GetTargetOrientation(waypoint, position);
|
||||
ImageRegion screen;
|
||||
Point2f position;
|
||||
int targetOrientation;
|
||||
Logger.LogInformation("精确接近目标点,位置({x2},{y2})", $"{waypoint.GameX:F1}", $"{waypoint.GameY:F1}");
|
||||
if (waypoint.MoveMode == MoveModeEnum.Fly.Code && waypoint.Action == ActionEnum.StopFlying.Code)
|
||||
{
|
||||
//下落攻击接近目的地
|
||||
Logger.LogInformation("动作:下落攻击");
|
||||
Simulation.SendInput.SimulateAction(GIActions.NormalAttack);
|
||||
await Delay(1000, ct);
|
||||
return;
|
||||
}
|
||||
|
||||
await _rotateTask.WaitUntilRotatedTo(targetOrientation, 2);
|
||||
var stepsTaken = 0;
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
@@ -857,6 +879,17 @@ public class PathExecutor
|
||||
await Delay(1000, ct);
|
||||
}
|
||||
|
||||
private async Task BeforeMoveCloseToTarget(WaypointForTrack waypoint)
|
||||
{
|
||||
if (waypoint.MoveMode == MoveModeEnum.Fly.Code && waypoint.Action == ActionEnum.StopFlying.Code)
|
||||
{
|
||||
//下落攻击接近目的地
|
||||
Logger.LogInformation("动作:下落攻击");
|
||||
Simulation.SendInput.SimulateAction(GIActions.NormalAttack);
|
||||
await Delay(1000, ct);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BeforeMoveToTarget(WaypointForTrack waypoint)
|
||||
{
|
||||
if (waypoint.Action == ActionEnum.UpDownGrabLeaf.Code)
|
||||
@@ -968,6 +1001,8 @@ public class PathExecutor
|
||||
{
|
||||
_autoSkipTrigger = new AutoSkipTrigger(new AutoSkipConfig
|
||||
{
|
||||
QuicklySkipConversationsEnabled = true, // 快速点击过剧情
|
||||
ClosePopupPagedEnabled = true,
|
||||
ClickChatOption = "优先选择最后一个选项",
|
||||
});
|
||||
_autoSkipTrigger.Init();
|
||||
|
||||
@@ -63,13 +63,11 @@ public class AutoTrackTask(AutoTrackParam param) : BaseIndependentTask
|
||||
catch (NormalEndException e)
|
||||
{
|
||||
Logger.LogInformation("自动追踪中断:" + e.Message);
|
||||
// NotificationHelper.SendTaskNotificationWithScreenshotUsing(b => b.Domain().Cancelled().Build());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError(e.Message);
|
||||
Logger.LogDebug(e.StackTrace);
|
||||
// NotificationHelper.SendTaskNotificationWithScreenshotUsing(b => b.Domain().Failure().Build());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoTrackPath;
|
||||
|
||||
@@ -9,47 +10,55 @@ public partial class TpConfig : ObservableObject
|
||||
private bool _mapZoomEnabled = true; // 地图缩放开关
|
||||
|
||||
[ObservableProperty]
|
||||
private int _mapZoomOutDistance = 2000; // 地图缩小的最小距离,单位:像素
|
||||
private int _mapZoomOutDistance = 1000; // 地图缩小的最小距离,单位:像素
|
||||
|
||||
[ObservableProperty]
|
||||
private int _mapZoomInDistance = 400; // 地图放大的最大距离,单位:像素
|
||||
|
||||
[ObservableProperty]
|
||||
private int _stepIntervalMilliseconds = 20; // 鼠标移动时间间隔,单位:ms
|
||||
|
||||
[ObservableProperty]
|
||||
private double _maxZoomLevel = 5.0; // 最大缩放等级
|
||||
private int _mapZoomInDistance = 400; // 地图放大的最大距离,单位:像素
|
||||
|
||||
[ObservableProperty]
|
||||
private double _minZoomLevel = 1.7; // 最小缩放等级
|
||||
|
||||
private int _stepIntervalMilliseconds = 20; // 鼠标移动时间间隔,单位:ms
|
||||
|
||||
[ObservableProperty]
|
||||
private double _reviveStatueOfTheSevenPointX = 2296.4; // 七天神像点位X坐标
|
||||
|
||||
private double _maxZoomLevel = 5.0; // 最大缩放等级
|
||||
|
||||
[ObservableProperty]
|
||||
private double _reviveStatueOfTheSevenPointY = -824.4; // 七天神像点位Y坐标
|
||||
|
||||
private double _minZoomLevel = 2.0; // 最小缩放等级
|
||||
|
||||
[ObservableProperty]
|
||||
private double _reviveStatueOfTheSevenPointX = 2296.4; // 七天神像点位X坐标
|
||||
|
||||
[ObservableProperty]
|
||||
private double _reviveStatueOfTheSevenPointY = -824.4; // 七天神像点位Y坐标
|
||||
|
||||
[ObservableProperty]
|
||||
[property: JsonIgnore]
|
||||
private int _zoomOutButtonY = 654; // y-coordinate for zoom-out button
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
private int _zoomInButtonY = 428; // y-coordinate for zoom-in button
|
||||
|
||||
[property: JsonIgnore]
|
||||
private int _zoomInButtonY = 428; // y-coordinate for zoom-in button
|
||||
|
||||
[ObservableProperty]
|
||||
[property: JsonIgnore]
|
||||
private int _zoomButtonX = 49; // x-coordinate for zoom button
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
[property: JsonIgnore]
|
||||
private int _zoomStartY = 453; // y-coordinate for zoom start
|
||||
|
||||
[ObservableProperty]
|
||||
[property: JsonIgnore]
|
||||
private int _zoomEndY = 628; // y-coordinate for zoom end
|
||||
|
||||
[ObservableProperty]
|
||||
private double _tolerance = 200; // 允许的移动误差
|
||||
|
||||
[ObservableProperty]
|
||||
private int _maxIterations = 30; // 移动最大次数
|
||||
|
||||
[ObservableProperty]
|
||||
private int _maxMouseMove = 300; // 单次移动最大距离
|
||||
|
||||
[ObservableProperty]
|
||||
private int _zoomEndY = 628; // y-coordinate for zoom end
|
||||
|
||||
[ObservableProperty]
|
||||
private double _tolerance = 200; // 允许的移动误差
|
||||
|
||||
[ObservableProperty]
|
||||
private int _maxIterations = 30; // 移动最大次数
|
||||
|
||||
[ObservableProperty]
|
||||
private int _maxMouseMove = 300; // 单次移动最大距离
|
||||
private double _mapScaleFactor = 2.661; // 游戏坐标和 mapZoomLevel=1 时的像素比例因子。
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -13,6 +14,7 @@ using BetterGenshinImpact.GameTask.Common;
|
||||
using BetterGenshinImpact.GameTask.Common.BgiVision;
|
||||
using BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||||
using BetterGenshinImpact.GameTask.Common.Exceptions;
|
||||
using BetterGenshinImpact.GameTask.Common.Job;
|
||||
using BetterGenshinImpact.GameTask.Common.Map;
|
||||
using BetterGenshinImpact.GameTask.Model.Area;
|
||||
using BetterGenshinImpact.GameTask.QuickTeleport.Assets;
|
||||
@@ -21,6 +23,7 @@ using Microsoft.Extensions.Logging;
|
||||
using OpenCvSharp;
|
||||
using Vanara.PInvoke;
|
||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||
using Log = Serilog.Log;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.AutoTrackPath;
|
||||
|
||||
@@ -33,7 +36,7 @@ public class TpTask(CancellationToken ct)
|
||||
private readonly Rect _captureRect = TaskContext.Instance().SystemInfo.ScaleMax1080PCaptureRect;
|
||||
private readonly double _zoomOutMax1080PRatio = TaskContext.Instance().SystemInfo.ZoomOutMax1080PRatio;
|
||||
private readonly TpConfig _tpConfig = TaskContext.Instance().Config.TpConfig;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 传送到须弥七天神像
|
||||
/// </summary>
|
||||
@@ -52,8 +55,10 @@ public class TpTask(CancellationToken ct)
|
||||
await AdjustMapZoomLevel(currentZoomLevel, 3);
|
||||
}
|
||||
}
|
||||
|
||||
await Tp(_tpConfig.ReviveStatueOfTheSevenPointX, _tpConfig.ReviveStatueOfTheSevenPointY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过大地图传送到指定坐标最近的传送点,然后移动到指定坐标
|
||||
/// </summary>
|
||||
@@ -62,29 +67,52 @@ public class TpTask(CancellationToken ct)
|
||||
/// <param name="force">强制以当前的tpX,tpY坐标进行自动传送</param>
|
||||
private async Task<(double, double)> TpOnce(double tpX, double tpY, bool force = false)
|
||||
{
|
||||
var (x, y) = (tpX, tpY);
|
||||
// 先确认在地图界面
|
||||
await CheckInBigMapUi();
|
||||
|
||||
string? country = null;
|
||||
if (!force)
|
||||
{
|
||||
// 获取最近的传送点位置
|
||||
(x, y, country) = GetRecentlyTpPoint(tpX, tpY);
|
||||
Logger.LogDebug("({TpX},{TpY}) 最近的传送点位置 ({X},{Y})", $"{tpX:F1}", $"{tpY:F1}", $"{x:F1}", $"{y:F1}");
|
||||
}
|
||||
// 传送前的计算准备
|
||||
var nTpPoints = GetNearestNTpPoints(tpX, tpY, 2);
|
||||
var (x, y) = force ? (tpX, tpY) : (nTpPoints[0].x, nTpPoints[0].y);
|
||||
var country = force ? null : nTpPoints[0].country;
|
||||
double disBetweenTpPoints = Math.Sqrt((nTpPoints[0].x - nTpPoints[1].x) * (nTpPoints[0].x - nTpPoints[1].x)
|
||||
+ (nTpPoints[0].y - nTpPoints[1].y) * (nTpPoints[0].y - nTpPoints[1].y));
|
||||
double minZoomLevel = Math.Max(disBetweenTpPoints / 20, 1);
|
||||
|
||||
|
||||
// 计算传送点位置离哪个地图切换后的中心点最近,切换到该地图
|
||||
await SwitchRecentlyCountryMap(x, y, country);
|
||||
|
||||
double zoomLevel = GetBigMapZoomLevel(CaptureToRectArea());
|
||||
if (_tpConfig.MapZoomEnabled)
|
||||
{
|
||||
double zoomLevel = GetBigMapZoomLevel(CaptureToRectArea());
|
||||
if (zoomLevel > 4.5)
|
||||
{
|
||||
// 显示传送锚点和秘境的缩放等级
|
||||
await AdjustMapZoomLevel(zoomLevel, 4.5);
|
||||
zoomLevel = 4.5;
|
||||
Logger.LogInformation("当前缩放等级过大,调整为 {zoomLevel:0.00}", 4.5);
|
||||
}
|
||||
}
|
||||
|
||||
if (zoomLevel > minZoomLevel)
|
||||
{
|
||||
if (_tpConfig.MapZoomEnabled)
|
||||
{
|
||||
Logger.LogInformation("目标传送点有相近传送点,到目标传送点附近将缩放到{zoomLevel:0.00}", minZoomLevel);
|
||||
await MoveMapTo(x, y, minZoomLevel);
|
||||
await Delay(300, ct); // 等待地图移动完成
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation("目标传送点有相近传送点,可能传送失败。如果失败请到设置-大地图地图传送设置开启地图缩放。");
|
||||
// TODO 部分无法区分点位强制缩放 即使没有zoomEnabled。
|
||||
// await AdjustMapZoomLevel(zoomLevel, minZoomLevel); // 临时修改缩放
|
||||
// await Delay(300, ct); // 等待缩放修改完成
|
||||
await MoveMapTo(x, y); // 移动地图
|
||||
await Delay(300, ct); // 等待地图移动完成
|
||||
// await AdjustMapZoomLevel(minZoomLevel, zoomLevel); // 恢复修改的缩放
|
||||
}
|
||||
}
|
||||
|
||||
var bigMapInAllMapRect = GetBigMapRect();
|
||||
while (!IsPointInBigMapWindow(bigMapInAllMapRect, x, y)) // 左上角 350x400也属于禁止点击区域
|
||||
{
|
||||
@@ -173,25 +201,27 @@ public class TpTask(CancellationToken ct)
|
||||
}
|
||||
|
||||
private async Task CheckInBigMapUi()
|
||||
{
|
||||
// 尝试打开地图失败后,先回到主界面后再次尝试打开地图
|
||||
if (!await TryToOpenBigMapUi())
|
||||
{
|
||||
await new ReturnMainUiTask().Start(ct);
|
||||
if (!await TryToOpenBigMapUi())
|
||||
{
|
||||
throw new RetryException("打开大地图失败,请检查按键绑定中「打开地图」按键设置是否和原神游戏中一致!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试打开地图界面
|
||||
/// </summary>
|
||||
private async Task<bool> TryToOpenBigMapUi()
|
||||
{
|
||||
// M 打开地图识别当前位置,中心点为当前位置
|
||||
var ra1 = CaptureToRectArea();
|
||||
if (!Bv.IsInBigMapUi(ra1))
|
||||
{
|
||||
// 不在主界面的情况下,先回到主界面
|
||||
if (!Bv.IsInMainUi(ra1))
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_ESCAPE);
|
||||
await Delay(1000, ct);
|
||||
ra1 = CaptureToRectArea();
|
||||
if (Bv.IsInMainUi(ra1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Simulation.SendInput.SimulateAction(GIActions.OpenMap);
|
||||
await Delay(1000, ct);
|
||||
for (int i = 0; i < 3; i++)
|
||||
@@ -201,12 +231,22 @@ public class TpTask(CancellationToken ct)
|
||||
{
|
||||
await Delay(500, ct);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<(double, double)> Tp(double tpX, double tpY, bool force = false)
|
||||
{
|
||||
await CheckInBigMapUi();
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
@@ -223,7 +263,6 @@ public class TpTask(CancellationToken ct)
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await CheckInBigMapUi();
|
||||
Logger.LogError("传送失败,重试 {I} 次", i + 1);
|
||||
Logger.LogDebug(e, "传送失败,重试 {I} 次", i + 1);
|
||||
}
|
||||
@@ -238,12 +277,13 @@ public class TpTask(CancellationToken ct)
|
||||
/// </summary>
|
||||
/// <param name="x">目标x坐标</param>
|
||||
/// <param name="y">目标y坐标</param>
|
||||
|
||||
private async Task MoveMapTo(double x, double y)
|
||||
/// <param name="minZoomLevel">到达目标点的最小缩放等级,只在 MapZoomEnabled 为 True 生效</param>
|
||||
private async Task MoveMapTo(double x, double y, double minZoomLevel = 2)
|
||||
{
|
||||
// 获取当前地图中心点并计算到目标传送点的初始偏移
|
||||
// await AdjustMapZoomLevel(mapZoomLevel);
|
||||
var bigMapCenterPoint = GetPositionFromBigMap(); // 初始中心
|
||||
minZoomLevel = Math.Min(minZoomLevel, _tpConfig.MinZoomLevel);
|
||||
var bigMapCenterPoint = GetPositionFromBigMap(); // 初始中心
|
||||
var newBigMapCenterPoint = bigMapCenterPoint;
|
||||
var (xOffset, yOffset) = (x - bigMapCenterPoint.X, y - bigMapCenterPoint.Y);
|
||||
int moveMouseX = 100 * Math.Sign(xOffset);
|
||||
@@ -264,56 +304,75 @@ public class TpTask(CancellationToken ct)
|
||||
{
|
||||
Logger.LogWarning("中心点识别失败,尝试预测移动的距离。");
|
||||
newBigMapCenterPoint = new Point2f(
|
||||
(float)(bigMapCenterPoint.X + xOffset * moveMouseX / totalMoveMouseX),
|
||||
(float)(bigMapCenterPoint.Y + yOffset * moveMouseY / totalMoveMouseY)
|
||||
(float)(bigMapCenterPoint.X + xOffset * moveMouseX / totalMoveMouseX),
|
||||
(float)(bigMapCenterPoint.Y + yOffset * moveMouseY / totalMoveMouseY)
|
||||
); // 利用移动鼠标的距离获取新的中心
|
||||
}
|
||||
|
||||
// 本次移动的距离
|
||||
double diffMapX = Math.Abs(newBigMapCenterPoint.X - bigMapCenterPoint.X);
|
||||
double diffMapY = Math.Abs(newBigMapCenterPoint.Y - bigMapCenterPoint.Y);
|
||||
|
||||
double moveDistance = Math.Sqrt(diffMapX * diffMapX + diffMapY * diffMapY);
|
||||
|
||||
if (moveDistance > 10) // 移动距离大于10认为本次移动成功
|
||||
{
|
||||
(xOffset, yOffset) = (x - newBigMapCenterPoint.X, y - newBigMapCenterPoint.Y); // 更新目标偏移量
|
||||
totalMoveMouseX = Math.Abs(moveMouseX * xOffset / diffMapX);
|
||||
totalMoveMouseY = Math.Abs(moveMouseY * yOffset / diffMapY);
|
||||
// totalMoveMouseX = Math.Abs(moveMouseX * xOffset / diffMapX);
|
||||
// totalMoveMouseY = Math.Abs(moveMouseY * yOffset / diffMapY);
|
||||
double currentZoomLevel = GetBigMapZoomLevel(CaptureToRectArea());
|
||||
totalMoveMouseX = _tpConfig.MapScaleFactor * Math.Abs(xOffset) / currentZoomLevel;
|
||||
totalMoveMouseY = _tpConfig.MapScaleFactor * Math.Abs(yOffset) / currentZoomLevel;
|
||||
double mouseDistance = Math.Sqrt(totalMoveMouseX * totalMoveMouseX + totalMoveMouseY * totalMoveMouseY);
|
||||
|
||||
if (_tpConfig.MapZoomEnabled)
|
||||
{
|
||||
// 调整地图缩放
|
||||
// mapZoomLevel<5 才显示传送锚点和秘境; mapZoomLevel>1.7 可以避免点错传送锚点
|
||||
double currentZoomLevel = GetBigMapZoomLevel(CaptureToRectArea());
|
||||
currentZoomLevel = GetBigMapZoomLevel(CaptureToRectArea());
|
||||
double oldZoomLevel = currentZoomLevel;
|
||||
|
||||
while (mouseDistance > _tpConfig.MapZoomOutDistance || mouseDistance < _tpConfig.MapZoomInDistance)
|
||||
{
|
||||
bool zoomOut = mouseDistance > _tpConfig.MapZoomOutDistance;
|
||||
bool zoomIn = mouseDistance < _tpConfig.MapZoomInDistance;
|
||||
if (zoomOut && currentZoomLevel < _tpConfig.MaxZoomLevel - 1.0
|
||||
|| zoomIn && currentZoomLevel > _tpConfig.MinZoomLevel + 1.0)
|
||||
bool zoomIn = mouseDistance < _tpConfig.MapZoomInDistance;
|
||||
if (zoomIn)
|
||||
{
|
||||
await AdjustMapZoomLevel(zoomIn);
|
||||
await Delay(50, ct);
|
||||
currentZoomLevel = GetBigMapZoomLevel(CaptureToRectArea());
|
||||
totalMoveMouseX *= oldZoomLevel / currentZoomLevel;
|
||||
totalMoveMouseY *= oldZoomLevel / currentZoomLevel;
|
||||
mouseDistance *= oldZoomLevel / currentZoomLevel;
|
||||
oldZoomLevel = currentZoomLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
double targetZoom = zoomIn ? _tpConfig.MinZoomLevel : _tpConfig.MaxZoomLevel;
|
||||
// 考虑调整和识别误差,所以相差0.05就不再调整。
|
||||
if (currentZoomLevel > _tpConfig.MaxZoomLevel - 0.05 || currentZoomLevel < _tpConfig.MinZoomLevel + 0.05)
|
||||
if (currentZoomLevel > _tpConfig.MinZoomLevel + 1.0)
|
||||
{
|
||||
await AdjustMapZoomLevel(zoomIn);
|
||||
await Delay(50, ct);
|
||||
}
|
||||
else if (currentZoomLevel < minZoomLevel + 0.05)
|
||||
{
|
||||
break;
|
||||
}
|
||||
await AdjustMapZoomLevel(currentZoomLevel, targetZoom);
|
||||
break;
|
||||
else
|
||||
{
|
||||
await AdjustMapZoomLevel(currentZoomLevel, minZoomLevel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currentZoomLevel < _tpConfig.MaxZoomLevel - 1.0)
|
||||
{
|
||||
await AdjustMapZoomLevel(zoomIn);
|
||||
await Delay(50, ct);
|
||||
}
|
||||
else if (currentZoomLevel > _tpConfig.MaxZoomLevel - 0.05)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
await AdjustMapZoomLevel(currentZoomLevel, _tpConfig.MaxZoomLevel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
currentZoomLevel = GetBigMapZoomLevel(CaptureToRectArea());
|
||||
totalMoveMouseX *= oldZoomLevel / currentZoomLevel;
|
||||
totalMoveMouseY *= oldZoomLevel / currentZoomLevel;
|
||||
mouseDistance *= oldZoomLevel / currentZoomLevel;
|
||||
oldZoomLevel = currentZoomLevel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +382,7 @@ public class TpTask(CancellationToken ct)
|
||||
Logger.LogInformation("移动 {I} 次鼠标后,已经接近目标点,不再移动地图。", iteration + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
moveMouseX = (int)Math.Min(totalMoveMouseX, _tpConfig.MaxMouseMove * totalMoveMouseX / mouseDistance) * Math.Sign(xOffset);
|
||||
moveMouseY = (int)Math.Min(totalMoveMouseY, _tpConfig.MaxMouseMove * totalMoveMouseY / mouseDistance) * Math.Sign(yOffset);
|
||||
double moveMouseLength = Math.Sqrt(moveMouseX * moveMouseX + moveMouseY * moveMouseY);
|
||||
@@ -373,7 +432,7 @@ public class TpTask(CancellationToken ct)
|
||||
await Delay(50, ct);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 调整地图的缩放等级(整数缩放级别)。
|
||||
/// </summary>
|
||||
@@ -437,7 +496,7 @@ public class TpTask(CancellationToken ct)
|
||||
// 随机起点以避免地图移动无效
|
||||
GameCaptureRegion.GameRegionMove((rect, _) =>
|
||||
(rect.Width / 2d + Random.Shared.Next(-rect.Width / 6, rect.Width / 6),
|
||||
rect.Height / 2d + Random.Shared.Next(-rect.Height / 6, rect.Height / 6)));
|
||||
rect.Height / 2d + Random.Shared.Next(-rect.Height / 6, rect.Height / 6)));
|
||||
|
||||
GlobalMethod.LeftButtonDown();
|
||||
for (var i = 0; i < steps; i++)
|
||||
@@ -445,10 +504,10 @@ public class TpTask(CancellationToken ct)
|
||||
GlobalMethod.MoveMouseBy(stepX[i], stepY[i]);
|
||||
await Delay(_tpConfig.StepIntervalMilliseconds, ct);
|
||||
}
|
||||
|
||||
GlobalMethod.LeftButtonUp();
|
||||
}
|
||||
|
||||
|
||||
public Point2f GetPositionFromBigMap()
|
||||
{
|
||||
return GetBigMapCenterPoint();
|
||||
@@ -529,6 +588,7 @@ public class TpTask(CancellationToken ct)
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
[Obsolete]
|
||||
public (double x, double y, string? country) GetRecentlyTpPoint(double x, double y)
|
||||
{
|
||||
double recentX = 0;
|
||||
@@ -546,9 +606,44 @@ public class TpTask(CancellationToken ct)
|
||||
country = tpPosition.Country;
|
||||
}
|
||||
}
|
||||
|
||||
return (recentX, recentY, country);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最接近的N个传送点坐标和所处区域
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <param name="n">获取最近的 n 个传送点</param>
|
||||
/// <returns></returns>
|
||||
public List<(double x, double y, string? country)> GetNearestNTpPoints(double x, double y, int n = 1)
|
||||
{
|
||||
// 检查 n 的合法性
|
||||
if (n < 1)
|
||||
{
|
||||
throw new ArgumentException("The value of n must be greater than or equal to 1.", nameof(n));
|
||||
}
|
||||
|
||||
// 按距离排序并选择前 n 个点
|
||||
var sortedTpPositions = MapLazyAssets.Instance.TpPositions
|
||||
.Select(tpPosition => new
|
||||
{
|
||||
tpPosition.X,
|
||||
tpPosition.Y,
|
||||
tpPosition.Country,
|
||||
Distance = Math.Sqrt(Math.Pow(tpPosition.X - x, 2) + Math.Pow(tpPosition.Y - y, 2))
|
||||
})
|
||||
.OrderBy(tp => tp.Distance)
|
||||
.Take(n) // 取前 n 个点
|
||||
.ToList();
|
||||
|
||||
// 将结果转换为 List<(x, y, country)>
|
||||
return sortedTpPositions
|
||||
.Select(tp => (tp.X, tp.Y, tp.Country))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<bool> SwitchRecentlyCountryMap(double x, double y, string? forceCountry = null)
|
||||
{
|
||||
// 可能是地下地图,切换到地上地图
|
||||
@@ -740,4 +835,4 @@ public class TpTask(CancellationToken ct)
|
||||
// 1~6 的缩放等级
|
||||
return (-5 * s) + 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ using BetterGenshinImpact.Core.Simulator;
|
||||
using BetterGenshinImpact.GameTask.AutoFight;
|
||||
using Vanara.PInvoke;
|
||||
using System.Threading;
|
||||
using BetterGenshinImpact.GameTask.AutoFight.Assets;
|
||||
using BetterGenshinImpact.GameTask.AutoSkip.Assets;
|
||||
using BetterGenshinImpact.GameTask.GameLoading.Assets;
|
||||
|
||||
@@ -148,7 +149,7 @@ public static partial class Bv
|
||||
/// <returns></returns>
|
||||
public static bool IsInRevivePrompt(ImageRegion region)
|
||||
{
|
||||
using var confirmRectArea = region.Find(AutoFightContext.Instance.FightAssets.ConfirmRa);
|
||||
using var confirmRectArea = region.Find(AutoFightAssets.Instance.ConfirmRa);
|
||||
if (!confirmRectArea.IsEmpty())
|
||||
{
|
||||
var list = region.FindMulti(new RecognitionObject
|
||||
|
||||
@@ -44,7 +44,7 @@ public partial class ChooseTalkOptionTask
|
||||
return TalkOptionRes.NotFound;
|
||||
}
|
||||
|
||||
await Task.Delay(200, ct);
|
||||
await Task.Delay(500, ct);
|
||||
|
||||
for (var i = 0; i < skipTimes; i++) // 重试3次
|
||||
{
|
||||
@@ -63,7 +63,7 @@ public partial class ChooseTalkOptionTask
|
||||
{
|
||||
if (isOrange)
|
||||
{
|
||||
// region.DeriveCrop(optionRa.ToRect()).SrcMat.SaveImage(Global.Absolute($"log\\t{optionRa.Text}.png"));
|
||||
region.DeriveCrop(optionRa.ToRect()).SrcMat.SaveImage(Global.Absolute($"log\\t{optionRa.Text}.png"));
|
||||
if (!IsOrangeOption(region.DeriveCrop(optionRa.ToRect()).SrcMat))
|
||||
{
|
||||
return TalkOptionRes.FoundButNotOrange;
|
||||
@@ -71,7 +71,7 @@ public partial class ChooseTalkOptionTask
|
||||
}
|
||||
|
||||
ClickOcrRegion(optionRa);
|
||||
await Task.Delay(200, ct);
|
||||
await Task.Delay(300, ct);
|
||||
return TalkOptionRes.FoundAndClick;
|
||||
}
|
||||
}
|
||||
@@ -198,16 +198,14 @@ public partial class ChooseTalkOptionTask
|
||||
private bool IsOrangeOption(Mat textMat)
|
||||
{
|
||||
// 只提取橙色
|
||||
using var bMat = OpenCvCommonHelper.Threshold(textMat, new Scalar(200, 165, 45), new Scalar(255, 205, 55));
|
||||
var whiteCount = OpenCvCommonHelper.CountGrayMatColor(bMat, 255);
|
||||
var rate = whiteCount * 1.0 / (bMat.Width * bMat.Height);
|
||||
Scalar lowerOrange = new Scalar(10, 150, 150);
|
||||
Scalar upperOrange = new Scalar(25, 255, 255);
|
||||
var mask = OpenCvCommonHelper.InRangeHsv(textMat, lowerOrange, upperOrange);
|
||||
int highConfidencePixels = Cv2.CountNonZero(mask);
|
||||
double rate = highConfidencePixels * 1.0 / (mask.Width * mask.Height);
|
||||
Debug.WriteLine($"识别到橙色文字区域占比:{rate}");
|
||||
if (rate > 0.06)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
_logger.LogInformation($"识别到橙色文字区域占比:{rate}");
|
||||
return rate > 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ public class GoToAdventurersGuildTask
|
||||
await new ReturnMainUiTask().Start(ct);
|
||||
|
||||
// 结束后重新打开
|
||||
await Delay(1000, ct);
|
||||
await Delay(1200, ct);
|
||||
var ra = CaptureToRectArea();
|
||||
if (!Bv.FindFAndPress(ra, "凯瑟琳"))
|
||||
{
|
||||
|
||||
@@ -102,6 +102,33 @@ namespace LogParse
|
||||
|
||||
if (configTask != null)
|
||||
{
|
||||
|
||||
//前往七天神像复活
|
||||
if (logstr.EndsWith("前往七天神像复活"))
|
||||
{
|
||||
configTask.Fault.ReviveCount++;
|
||||
}
|
||||
//传送失败,重试 n 次
|
||||
result = parseBgiLine($@"传送失败,重试 (\d+) 次", logstr);
|
||||
if (result.Item1)
|
||||
{
|
||||
configTask.Fault.TeleportFailCount = int.Parse(result.Item2[1]);
|
||||
|
||||
}
|
||||
//战斗超时结束
|
||||
if (logstr == "战斗超时结束")
|
||||
{
|
||||
configTask.Fault.BattleTimeoutCount ++;
|
||||
}
|
||||
|
||||
//重试一次路线或放弃此路线!
|
||||
if (logstr.EndsWith("重试一次路线或放弃此路线!"))
|
||||
{
|
||||
configTask.Fault.RetryCount++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (logstr.StartsWith("→ 脚本执行结束: \"" + configTask.Name + "\""))
|
||||
{
|
||||
configTask.EndDate = parsePreDataTime(logLines, i - 1, logrq);
|
||||
@@ -182,6 +209,8 @@ namespace LogParse
|
||||
//配置人物列表xxx.json
|
||||
public List<ConfigTask> ConfigTaskList { get; } = new();
|
||||
|
||||
|
||||
|
||||
public class ConfigTask
|
||||
{
|
||||
public string Name { get; set; }
|
||||
@@ -204,6 +233,21 @@ namespace LogParse
|
||||
|
||||
Picks[val] = Picks[val] + 1;
|
||||
}
|
||||
public FaultScenario Fault { get; set; } = new();
|
||||
|
||||
public class FaultScenario
|
||||
{
|
||||
//复活次数
|
||||
public int ReviveCount { get; set; } = 0;
|
||||
//传送失败次数
|
||||
public int TeleportFailCount { get; set; } = 0;
|
||||
//重试次数
|
||||
public int RetryCount { get; set; } = 0;
|
||||
//战斗超时
|
||||
public int BattleTimeoutCount { get; set; } = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +351,42 @@ namespace LogParse
|
||||
return customDayStart;
|
||||
}
|
||||
|
||||
public static string GenerHtmlByConfigGroupEntity(List<ConfigGroupEntity> configGroups, GameInfo gameInfo)
|
||||
public static string FormatNumberWithStyle(int a, int b=3)
|
||||
{
|
||||
if (a== 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
// Determine the style based on the condition
|
||||
string colorStyle = a >= b ? "color:red;" : string.Empty;
|
||||
|
||||
// Return the formatted HTML string
|
||||
return $"<span style=\"font-weight:bold;{colorStyle}\">{a}</span>";
|
||||
}
|
||||
public static string GetNumberOrEmptyString(int number)
|
||||
{
|
||||
// 如果数字为0,返回空字符串,否则返回数字的字符串形式
|
||||
return number == 0 ? string.Empty : number.ToString();
|
||||
}
|
||||
public static string SubtractFiveSeconds(string inputTime,int seconds)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 将输入的字符串解析为 DateTime
|
||||
DateTime parsedTime = DateTime.ParseExact(inputTime, "yyyy-MM-dd HH:mm:ss", null);
|
||||
|
||||
// 减去 5 秒
|
||||
DateTime resultTime = parsedTime.AddSeconds(-seconds);
|
||||
|
||||
// 转换回指定格式的字符串并返回
|
||||
return resultTime.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return "Invalid input time format. Please use 'yyyy-MM-dd HH:mm:ss'.";
|
||||
}
|
||||
}
|
||||
public static string GenerHtmlByConfigGroupEntity(List<ConfigGroupEntity> configGroups, GameInfo? gameInfo,LogParseConfig.ScriptGroupLogParseConfig scriptGroupLogParseConfig)
|
||||
{
|
||||
(string name, Func<ConfigTask, string> value)[] colConfigs =
|
||||
[
|
||||
@@ -316,20 +395,28 @@ namespace LogParse
|
||||
(name: "结束日期", value: task => task.EndDate?.ToString("yyyy-MM-dd HH:mm:ss") ?? ""),
|
||||
(name: "耗时", value: task => ConvertSecondsToTime((task.EndDate - task.StartDate)?.TotalSeconds ?? 0))
|
||||
];
|
||||
|
||||
|
||||
List<(string name, Func<ConfigTask, string> value)> colConfigList = new();
|
||||
colConfigList.AddRange(colConfigs);
|
||||
if (scriptGroupLogParseConfig.FaultStatsSwitch)
|
||||
{
|
||||
colConfigList.Add((name: "复活次数", value: task => FormatNumberWithStyle(task.Fault.ReviveCount)));
|
||||
colConfigList.Add((name: "重试次数", value: task => FormatNumberWithStyle(task.Fault.RetryCount)));
|
||||
colConfigList.Add((name: "战斗超时次数", value: task => FormatNumberWithStyle(task.Fault.BattleTimeoutCount)));
|
||||
colConfigList.Add((name: "传送失败次数", value: task => FormatNumberWithStyle(task.Fault.TeleportFailCount)));
|
||||
}
|
||||
|
||||
|
||||
(string name, Func<MoraStatistics, string> value)[] msColConfigs =
|
||||
[
|
||||
(name: "日期", value: ms => ms.Name), (name: "小怪", value: ms => ms.SmallMonsterStatistics.ToString()),
|
||||
(name: "日期", value: ms => ms.Name), (name: "小怪", value: ms => GetNumberOrEmptyString(ms.SmallMonsterStatistics)),
|
||||
(name: "最后小怪日期", value: ms => ms.LastSmallTime),
|
||||
(name: "精英", value: ms => ms.EliteGameStatistics.ToString()),
|
||||
(name: "精英", value: ms => GetNumberOrEmptyString(ms.EliteGameStatistics)),
|
||||
(name: "精英详细", value: ms => ms.EliteDetails), (name: "最后精英日期", value: ms => ms.LastEliteTime),
|
||||
(name: "总计锄地摩拉", value: ms => ms.TotalMoraKillingMonstersMora.ToString()),
|
||||
(name: "突发事件获取摩拉", value: ms => ms.EmergencyBonus)
|
||||
];
|
||||
//锄地部分新曾字段
|
||||
(string name, Func<MoraStatistics, string> value)[] col2Configs=[..msColConfigs.ToList().Where(item=>item.name!="日期" && item.name!="最后小怪日期" && item.name!="最后精英日期"),
|
||||
(string name, Func<MoraStatistics, string> value)[] col2Configs=[..msColConfigs.ToList().Where(item=>item.name!="日期" && item.name!="最后小怪日期" && item.name!="最后精英日期" && item.name!="突发事件获取摩拉"),
|
||||
(name: "摩拉(每秒)", value: ms => (ms.TotalMoraKillingMonstersMora/(ms.StatisticsEnd-ms.StatisticsStart)?.TotalSeconds ?? 0).ToString("F2")),
|
||||
];
|
||||
|
||||
@@ -340,11 +427,19 @@ namespace LogParse
|
||||
List<ActionItem> actionItems = new();
|
||||
if (gameInfo != null)
|
||||
{
|
||||
|
||||
actionItems = TravelsDiaryDetailManager.loadAllActionItems(gameInfo, configGroups);
|
||||
int hoeingDelay;
|
||||
if (int.TryParse(scriptGroupLogParseConfig.HoeingDelay, out hoeingDelay))
|
||||
{
|
||||
foreach (var actionItem in actionItems)
|
||||
{
|
||||
actionItem.Time = SubtractFiveSeconds(actionItem.Time,hoeingDelay);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return GenerHtmlByConfigGroupEntity(configGroups, "日志分析", colConfigs,col2Configs, actionItems, msColConfigs);
|
||||
return GenerHtmlByConfigGroupEntity(configGroups, "日志分析", colConfigList.ToArray(),col2Configs, actionItems, msColConfigs);
|
||||
}
|
||||
public static string ConcatenateStrings(string a, string b)
|
||||
{
|
||||
@@ -373,6 +468,8 @@ namespace LogParse
|
||||
html.AppendLine(" table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }");
|
||||
html.AppendLine(" th, td { border: 1px solid black; padding: 8px; text-align: left; }");
|
||||
html.AppendLine(" th { background-color: #f2f2f2; }");
|
||||
html.AppendLine(" tr:nth-child(odd) { background-color: #eaeaea; /* 奇数行颜色 */ }");
|
||||
html.AppendLine(" tr:nth-child(even) { background-color: #f9f9f9; /* 偶数行颜色 */}");
|
||||
html.AppendLine(" </style>");
|
||||
html.AppendLine("</head>");
|
||||
html.AppendLine("<body>");
|
||||
|
||||
@@ -14,5 +14,7 @@ public partial class LogParseConfig : ObservableObject
|
||||
[ObservableProperty] private string _rangeValue = "CurrentConfig";
|
||||
[ObservableProperty] private string _dayRangeValue = "7";
|
||||
[ObservableProperty] private bool _hoeingStatsSwitch = false;
|
||||
[ObservableProperty] private bool _faultStatsSwitch = false;
|
||||
[ObservableProperty] private string _hoeingDelay= "0";
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,12 @@ namespace LogParse
|
||||
{
|
||||
var ls = this.ActionItems.Where(item => item.ActionId == 28).ToList();
|
||||
var count = ls.Count();
|
||||
return ls.Sum(item=>item.Num)+((count==0 || count>=10)?"":$"({count}/10)");
|
||||
if (count == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return ls.Sum(item=>item.Num)+(count>=10?"":$"({count}/10)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ using BetterGenshinImpact.Helpers;
|
||||
using Wpf.Ui.Violeta.Controls;
|
||||
using static BetterGenshinImpact.GameTask.Common.TaskControl;
|
||||
using BetterGenshinImpact.Service;
|
||||
using BetterGenshinImpact.Service.Notification;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask;
|
||||
|
||||
@@ -48,17 +50,13 @@ public class TaskRunner
|
||||
_logger.LogError("任务启动失败:当前存在正在运行中的独立任务,请不要重复执行任务!");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("→ {Text}", _name + "任务启动!");
|
||||
|
||||
// 初始化
|
||||
Init();
|
||||
|
||||
// 发送运行任务通知
|
||||
SendNotification();
|
||||
|
||||
|
||||
CancellationContext.Instance.Set();
|
||||
RunnerContext.Instance.Clear();
|
||||
|
||||
@@ -66,8 +64,8 @@ public class TaskRunner
|
||||
}
|
||||
catch (NormalEndException e)
|
||||
{
|
||||
Notify.Event(NotificationEvent.TaskCancel).Success("任务手动取消,或正常结束");
|
||||
_logger.LogInformation("任务中断:{Msg}", e.Message);
|
||||
SendNotification();
|
||||
if (RunnerContext.Instance.IsContinuousRunGroup)
|
||||
{
|
||||
// 连续执行时,抛出异常,终止执行
|
||||
@@ -76,8 +74,8 @@ public class TaskRunner
|
||||
}
|
||||
catch (TaskCanceledException e)
|
||||
{
|
||||
Notify.Event(NotificationEvent.TaskCancel).Success("任务被手动取消");
|
||||
_logger.LogInformation("任务中断:{Msg}", "任务被取消");
|
||||
SendNotification();
|
||||
if (RunnerContext.Instance.IsContinuousRunGroup)
|
||||
{
|
||||
// 连续执行时,抛出异常,终止执行
|
||||
@@ -86,15 +84,14 @@ public class TaskRunner
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Notify.Event(NotificationEvent.TaskError).Error("任务执行异常", e);
|
||||
_logger.LogError(e.Message);
|
||||
_logger.LogDebug(e.StackTrace);
|
||||
SendNotification();
|
||||
}
|
||||
finally
|
||||
{
|
||||
End();
|
||||
_logger.LogInformation("→ {Text}", _name + "任务结束");
|
||||
SendNotification();
|
||||
|
||||
CancellationContext.Instance.Clear();
|
||||
RunnerContext.Instance.Clear();
|
||||
@@ -120,7 +117,8 @@ public class TaskRunner
|
||||
public async Task RunSoloTaskAsync(ISoloTask soloTask)
|
||||
{
|
||||
// 没启动的时候先启动
|
||||
await ScriptService.StartGameTask();
|
||||
bool waitForMainUi = soloTask.Name != "自动七圣召唤" && !soloTask.Name.Contains("自动音游");
|
||||
await ScriptService.StartGameTask(waitForMainUi);
|
||||
await Task.Run(() => RunCurrentAsync(async () => await soloTask.Start(CancellationContext.Instance.Cts.Token)));
|
||||
}
|
||||
|
||||
@@ -190,7 +188,4 @@ public class TaskRunner
|
||||
}
|
||||
}
|
||||
|
||||
public void SendNotification()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
62
BetterGenshinImpact/Genshin/Settings2/GameSettingsChecker.cs
Normal file
62
BetterGenshinImpact/Genshin/Settings2/GameSettingsChecker.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using BetterGenshinImpact.Genshin.Settings;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BetterGenshinImpact.Genshin.Settings2;
|
||||
|
||||
public class GameSettingsChecker
|
||||
{
|
||||
public static void LoadGameSettingsAndCheck()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settingStr = GenshinGameSettings.GetStrFromRegistry();
|
||||
if (settingStr == null)
|
||||
{
|
||||
TaskControl.Logger.LogDebug("获取原神游戏设置失败");
|
||||
return;
|
||||
}
|
||||
|
||||
GenshinGameSettings? settings = GenshinGameSettings.Parse(settingStr);
|
||||
if (settings == null)
|
||||
{
|
||||
TaskControl.Logger.LogDebug("获取原神游戏设置失败");
|
||||
return;
|
||||
}
|
||||
|
||||
GenshinGameInputSettings? inputSettings = GenshinGameInputSettings.Parse(settings.InputData);
|
||||
if (inputSettings == null)
|
||||
{
|
||||
TaskControl.Logger.LogError("获取原神游戏输入设置失败");
|
||||
return;
|
||||
}
|
||||
|
||||
if (settings.GammaValue != "2.200000047683716")
|
||||
{
|
||||
TaskControl.Logger.LogError("检测到游戏亮度非默认值,将会影响功能正常使用,请在原神 游戏设置——图像——亮度 中恢复默认亮度!");
|
||||
}
|
||||
|
||||
if (inputSettings.MouseSenseIndex != 2
|
||||
|| inputSettings.MouseSenseIndexY != 2
|
||||
|| inputSettings.MouseFocusSenseIndex != 2
|
||||
|| inputSettings.MouseFocusSenseIndexY != 2)
|
||||
{
|
||||
TaskControl.Logger.LogInformation("当前:镜头水平灵敏度{X1},镜头垂直灵敏度{Y1},镜头水平灵敏度(瞄准模式){X2},镜头垂直灵敏度(瞄准模式){Y2}",
|
||||
inputSettings.MouseSenseIndex + 1, inputSettings.MouseSenseIndexY + 1,
|
||||
inputSettings.MouseFocusSenseIndex + 1, inputSettings.MouseFocusSenseIndexY + 1);
|
||||
TaskControl.Logger.LogError("检测到镜头灵敏度不是默认值3,将会影响所有视角移动功能的正常使用,请在原神 游戏设置——控制 中恢复默认灵敏度!");
|
||||
}
|
||||
|
||||
var lang = (TextLanguage)settings.DeviceLanguageType;
|
||||
if (lang != TextLanguage.SimplifiedChinese)
|
||||
{
|
||||
TaskControl.Logger.LogWarning("当前游戏语言{Lang}不是简体中文,部分功能可能无法正常使用。The game language is not Simplified Chinese, some functions may not work properly", lang);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TaskControl.Logger.LogDebug(e, "获取原神游戏设置失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BetterGenshinImpact.Genshin.Settings2;
|
||||
|
||||
public class GenshinGameInputSettings
|
||||
{
|
||||
[JsonProperty("scriptVersion")]
|
||||
public string ScriptVersion { get; set; } // 脚本版本
|
||||
|
||||
[JsonProperty("mouseSensitivity")]
|
||||
public string MouseSensitivity { get; set; } // 鼠标灵敏度
|
||||
|
||||
[JsonProperty("joypadSenseIndex")]
|
||||
public int JoypadSenseIndex { get; set; } // 手柄灵敏度索引
|
||||
|
||||
[JsonProperty("joypadFocusSenseIndex")]
|
||||
public int JoypadFocusSenseIndex { get; set; } // 手柄焦点灵敏度索引
|
||||
|
||||
[JsonProperty("joypadInvertCameraX")]
|
||||
public bool JoypadInvertCameraX { get; set; } // 手柄X轴反转摄像头
|
||||
|
||||
[JsonProperty("joypadInvertCameraY")]
|
||||
public bool JoypadInvertCameraY { get; set; } // 手柄Y轴反转摄像头
|
||||
|
||||
[JsonProperty("joypadInvertFocusCameraX")]
|
||||
public bool JoypadInvertFocusCameraX { get; set; } // 手柄X轴反转焦点摄像头
|
||||
|
||||
[JsonProperty("joypadInvertFocusCameraY")]
|
||||
public bool JoypadInvertFocusCameraY { get; set; } // 手柄Y轴反转焦点摄像头
|
||||
|
||||
[JsonProperty("mouseSenseIndex")]
|
||||
public int MouseSenseIndex { get; set; } // 鼠标灵敏度索引
|
||||
|
||||
[JsonProperty("mouseFocusSenseIndex")]
|
||||
public int MouseFocusSenseIndex { get; set; } // 鼠标焦点灵敏度索引
|
||||
|
||||
[JsonProperty("touchpadSenseIndex")]
|
||||
public int TouchpadSenseIndex { get; set; } // 触摸板灵敏度索引
|
||||
|
||||
[JsonProperty("touchpadFocusSenseIndex")]
|
||||
public int TouchpadFocusSenseIndex { get; set; } // 触摸板焦点灵敏度索引
|
||||
|
||||
[JsonProperty("enableTouchpadFocusAcceleration")]
|
||||
public bool EnableTouchpadFocusAcceleration { get; set; } // 启用触摸板焦点加速度
|
||||
|
||||
[JsonProperty("lastJoypadDefaultScale")]
|
||||
public float LastJoypadDefaultScale { get; set; } // 最后一次手柄默认缩放
|
||||
|
||||
[JsonProperty("lastJoypadFocusScale")]
|
||||
public float LastJoypadFocusScale { get; set; } // 最后一次手柄焦点缩放
|
||||
|
||||
[JsonProperty("lastPCDefaultScale")]
|
||||
public float LastPCDefaultScale { get; set; } // 最后一次PC默认缩放
|
||||
|
||||
[JsonProperty("lastPCFocusScale")]
|
||||
public float LastPCFocusScale { get; set; } // 最后一次PC焦点缩放
|
||||
|
||||
[JsonProperty("lastTouchDefaultScale")]
|
||||
public float LastTouchDefaultScale { get; set; } // 最后一次触摸默认缩放
|
||||
|
||||
[JsonProperty("lastTouchFcousScale")]
|
||||
public float LastTouchFocusScale { get; set; } // 最后一次触摸焦点缩放
|
||||
|
||||
[JsonProperty("switchWalkRunByBtn")]
|
||||
public bool SwitchWalkRunByBtn { get; set; } // 通过按钮切换行走和奔跑
|
||||
|
||||
[JsonProperty("skiffCameraAutoFix")]
|
||||
public bool SkiffCameraAutoFix { get; set; } // 小艇摄像头自动修正
|
||||
|
||||
[JsonProperty("skiffCameraAutoFixInCombat")]
|
||||
public bool SkiffCameraAutoFixInCombat { get; set; } // 战斗中小艇摄像头自动修正
|
||||
|
||||
[JsonProperty("cameraDistanceRatio")]
|
||||
public float CameraDistanceRatio { get; set; } // 摄像头距离比率
|
||||
|
||||
[JsonProperty("wwiseVibration")]
|
||||
public bool WwiseVibration { get; set; } // Wwise振动
|
||||
|
||||
[JsonProperty("isYInited")]
|
||||
public bool IsYInited { get; set; } // Y轴是否初始化
|
||||
|
||||
[JsonProperty("joypadSenseIndexY")]
|
||||
public int JoypadSenseIndexY { get; set; } // 手柄Y轴灵敏度索引
|
||||
|
||||
[JsonProperty("joypadFocusSenseIndexY")]
|
||||
public int JoypadFocusSenseIndexY { get; set; } // 手柄Y轴焦点灵敏度索引
|
||||
|
||||
[JsonProperty("mouseSenseIndexY")]
|
||||
public int MouseSenseIndexY { get; set; } // 鼠标Y轴灵敏度索引
|
||||
|
||||
[JsonProperty("mouseFocusSenseIndexY")]
|
||||
public int MouseFocusSenseIndexY { get; set; } // 鼠标Y轴焦点灵敏度索引
|
||||
|
||||
[JsonProperty("touchpadSenseIndexY")]
|
||||
public int TouchpadSenseIndexY { get; set; } // 触摸板Y轴灵敏度索引
|
||||
|
||||
[JsonProperty("touchpadFocusSenseIndexY")]
|
||||
public int TouchpadFocusSenseIndexY { get; set; } // 触摸板Y轴焦点灵敏度索引
|
||||
|
||||
[JsonProperty("lastJoypadDefaultScaleY")]
|
||||
public float LastJoypadDefaultScaleY { get; set; } // 最后一次手柄Y轴默认缩放
|
||||
|
||||
[JsonProperty("lastJoypadFocusScaleY")]
|
||||
public float LastJoypadFocusScaleY { get; set; } // 最后一次手柄Y轴焦点缩放
|
||||
|
||||
[JsonProperty("lastPCDefaultScaleY")]
|
||||
public float LastPCDefaultScaleY { get; set; } // 最后一次PC Y轴默认缩放
|
||||
|
||||
[JsonProperty("lastPCFocusScaleY")]
|
||||
public float LastPCFocusScaleY { get; set; } // 最后一次PC Y轴焦点缩放
|
||||
|
||||
[JsonProperty("lastTouchDefaultScaleY")]
|
||||
public float LastTouchDefaultScaleY { get; set; } // 最后一次触摸Y轴默认缩放
|
||||
|
||||
[JsonProperty("lastTouchFcousScaleY")]
|
||||
public float LastTouchFocusScaleY { get; set; } // 最后一次触摸Y轴焦点缩放
|
||||
|
||||
public static GenshinGameInputSettings? Parse(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<GenshinGameInputSettings>(json);
|
||||
}
|
||||
}
|
||||
360
BetterGenshinImpact/Genshin/Settings2/GenshinGameSettings.cs
Normal file
360
BetterGenshinImpact/Genshin/Settings2/GenshinGameSettings.cs
Normal file
@@ -0,0 +1,360 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using BetterGenshinImpact.Genshin.Settings;
|
||||
using Microsoft.Win32;
|
||||
using Newtonsoft.Json;
|
||||
using JsonSerializerOptions = System.Text.Json.JsonSerializerOptions;
|
||||
|
||||
namespace BetterGenshinImpact.Genshin.Settings2;
|
||||
|
||||
public class GenshinGameSettings
|
||||
{
|
||||
[JsonProperty("deviceUUID")]
|
||||
public string DeviceUUID { get; set; } // 设备唯一标识符
|
||||
|
||||
[JsonProperty("userLocalDataVersionId")]
|
||||
public string UserLocalDataVersionId { get; set; } // 用户本地数据版本ID
|
||||
|
||||
[JsonProperty("deviceLanguageType")]
|
||||
public int DeviceLanguageType { get; set; } // 设备语言类型
|
||||
|
||||
[JsonProperty("deviceVoiceLanguageType")]
|
||||
public int DeviceVoiceLanguageType { get; set; } // 设备语音语言类型
|
||||
|
||||
[JsonProperty("selectedServerName")]
|
||||
public string SelectedServerName { get; set; } // 选择的服务器名称
|
||||
|
||||
[JsonProperty("localLevelIndex")]
|
||||
public int LocalLevelIndex { get; set; } // 本地等级索引
|
||||
|
||||
[JsonProperty("deviceID")]
|
||||
public string DeviceID { get; set; } // 设备ID
|
||||
|
||||
[JsonProperty("targetUID")]
|
||||
public string TargetUID { get; set; } // 目标用户ID
|
||||
|
||||
[JsonProperty("curAccountName")]
|
||||
public string CurAccountName { get; set; } // 当前账户名称
|
||||
|
||||
[JsonProperty("uiSaveData")]
|
||||
public string UiSaveData { get; set; } // UI保存数据
|
||||
|
||||
[JsonProperty("inputData")]
|
||||
public string InputData { get; set; } // 输入设置数据
|
||||
|
||||
[JsonProperty("graphicsData")]
|
||||
public string GraphicsData { get; set; } // 图形设置数据
|
||||
|
||||
[JsonProperty("globalPerfData")]
|
||||
public string GlobalPerfData { get; set; } // 全局性能数据
|
||||
|
||||
[JsonProperty("miniMapConfig")]
|
||||
public int MiniMapConfig { get; set; } // 小地图配置
|
||||
|
||||
[JsonProperty("enableCameraSlope")]
|
||||
public bool EnableCameraSlope { get; set; } // 启用相机坡度
|
||||
|
||||
[JsonProperty("enableCameraCombatLock")]
|
||||
public bool EnableCameraCombatLock { get; set; } // 启用相机战斗锁定
|
||||
|
||||
[JsonProperty("completionPkg")]
|
||||
public bool CompletionPkg { get; set; } // 完成的包
|
||||
|
||||
[JsonProperty("completionPlayGoPkg")]
|
||||
public bool CompletionPlayGoPkg { get; set; } // 完成的PlayGo包
|
||||
|
||||
[JsonProperty("onlyPlayWithPSPlayer")]
|
||||
public bool OnlyPlayWithPSPlayer { get; set; } // 仅与PS玩家一起游戏
|
||||
|
||||
[JsonProperty("onlyPlayWithXboxPlayer")]
|
||||
public bool OnlyPlayWithXboxPlayer { get; set; } // 仅与Xbox玩家一起游戏
|
||||
|
||||
[JsonProperty("needPlayGoFullPkgPatch")]
|
||||
public bool NeedPlayGoFullPkgPatch { get; set; } // 需要PlayGo完整包补丁
|
||||
|
||||
[JsonProperty("resinNotification")]
|
||||
public bool ResinNotification { get; set; } // 树脂通知
|
||||
|
||||
[JsonProperty("exploreNotification")]
|
||||
public bool ExploreNotification { get; set; } // 探索通知
|
||||
|
||||
[JsonProperty("volumeGlobal")]
|
||||
public int VolumeGlobal { get; set; } // 全局音量
|
||||
|
||||
[JsonProperty("volumeSFX")]
|
||||
public int VolumeSFX { get; set; } // 音效音量
|
||||
|
||||
[JsonProperty("volumeMusic")]
|
||||
public int VolumeMusic { get; set; } // 音乐音量
|
||||
|
||||
[JsonProperty("volumeVoice")]
|
||||
public int VolumeVoice { get; set; } // 语音音量
|
||||
|
||||
[JsonProperty("audioAPI")]
|
||||
public int AudioAPI { get; set; } // 音频API
|
||||
|
||||
[JsonProperty("audioDynamicRange")]
|
||||
public int AudioDynamicRange { get; set; } // 音频动态范围
|
||||
|
||||
[JsonProperty("audioOutput")]
|
||||
public int AudioOutput { get; set; } // 音频输出
|
||||
|
||||
[JsonProperty("_audioSuccessInit")]
|
||||
public bool AudioSuccessInit { get; set; } // 音频成功初始化
|
||||
|
||||
[JsonProperty("enableAudioChangeAndroidMinimumBufferCapacity")]
|
||||
public bool EnableAudioChangeAndroidMinimumBufferCapacity { get; set; } // 启用更改Android最小缓冲容量的音频
|
||||
|
||||
[JsonProperty("audioAndroidMiniumBufferCapacity")]
|
||||
public int AudioAndroidMiniumBufferCapacity { get; set; } // Android音频最小缓冲容量
|
||||
|
||||
[JsonProperty("vibrationLevel")]
|
||||
public int VibrationLevel { get; set; } // 震动等级
|
||||
|
||||
[JsonProperty("vibrationIntensity")]
|
||||
public int VibrationIntensity { get; set; } // 震动强度
|
||||
|
||||
[JsonProperty("usingNewVibrationSetting")]
|
||||
public bool UsingNewVibrationSetting { get; set; } // 使用新的震动设置
|
||||
|
||||
[JsonProperty("motionBlur")]
|
||||
public bool MotionBlur { get; set; } // 动态模糊
|
||||
|
||||
[JsonProperty("gyroAiming")]
|
||||
public bool GyroAiming { get; set; } // 陀螺仪瞄准
|
||||
|
||||
[JsonProperty("gyroHorMoveSpeedIndex")]
|
||||
public int GyroHorMoveSpeedIndex { get; set; } // 陀螺仪水平移动速度索引
|
||||
|
||||
[JsonProperty("gyroVerMoveSpeedIndex")]
|
||||
public int GyroVerMoveSpeedIndex { get; set; } // 陀螺仪垂直移动速度索引
|
||||
|
||||
[JsonProperty("gyroHorReverse")]
|
||||
public bool GyroHorReverse { get; set; } // 陀螺仪水平反转
|
||||
|
||||
[JsonProperty("gyroVerReverse")]
|
||||
public bool GyroVerReverse { get; set; } // 陀螺仪垂直反转
|
||||
|
||||
[JsonProperty("gyroRotateType")]
|
||||
public int GyroRotateType { get; set; } // 陀螺仪旋转类型
|
||||
|
||||
[JsonProperty("gyroExcludeRightStickVerInput")]
|
||||
public bool GyroExcludeRightStickVerInput { get; set; } // 陀螺仪排除右摇杆垂直输入
|
||||
|
||||
[JsonProperty("firstHDRSetting")]
|
||||
public bool FirstHDRSetting { get; set; } // 首次HDR设置
|
||||
|
||||
[JsonProperty("maxLuminosity")]
|
||||
public float MaxLuminosity { get; set; } // 最大亮度
|
||||
|
||||
[JsonProperty("uiPaperWhite")]
|
||||
public float UiPaperWhite { get; set; } // UI纸白
|
||||
|
||||
[JsonProperty("scenePaperWhite")]
|
||||
public float ScenePaperWhite { get; set; } // 场景纸白
|
||||
|
||||
/// <summary>
|
||||
/// 2.200000047683716
|
||||
/// </summary>
|
||||
[JsonProperty("gammaValue")]
|
||||
public string GammaValue { get; set; } // 伽马值
|
||||
|
||||
[JsonProperty("enableHDR")]
|
||||
public bool EnableHDR { get; set; } // 启用HDR
|
||||
|
||||
[JsonProperty("_overrideControllerMapKeyList")]
|
||||
public List<string> OverrideControllerMapKeyList { get; set; } // 覆盖控制器映射键列表
|
||||
|
||||
[JsonProperty("_overrideControllerMapValueList")]
|
||||
public List<string> OverrideControllerMapValueList { get; set; } // 覆盖控制器映射值列表
|
||||
|
||||
[JsonProperty("rewiredMapMigrateRecord")]
|
||||
public List<string> RewiredMapMigrateRecord { get; set; } // 重布线映射迁移记录
|
||||
|
||||
[JsonProperty("rewiredDisableKeyboard")]
|
||||
public bool RewiredDisableKeyboard { get; set; } // 重布线禁用键盘
|
||||
|
||||
[JsonProperty("rewiredEnableKeyboard")]
|
||||
public bool RewiredEnableKeyboard { get; set; } // 重布线启用键盘
|
||||
|
||||
[JsonProperty("rewiredEnableEDS")]
|
||||
public bool RewiredEnableEDS { get; set; } // 重布线启用EDS
|
||||
|
||||
[JsonProperty("disableRewiredDelayInit")]
|
||||
public bool DisableRewiredDelayInit { get; set; } // 禁用重布线延迟初始化
|
||||
|
||||
[JsonProperty("disableRewiredInitProtection")]
|
||||
public bool DisableRewiredInitProtection { get; set; } // 禁用重布线初始化保护
|
||||
|
||||
[JsonProperty("disableSetJoyInfoForWebViewOnPCMobile")]
|
||||
public bool DisableSetJoyInfoForWebViewOnPCMobile { get; set; } // 禁用在PC和移动设备上的WebView设置Joy信息
|
||||
|
||||
[JsonProperty("conflictKeyBindingElementId")]
|
||||
public List<int> ConflictKeyBindingElementId { get; set; } // 冲突的按键绑定元素ID
|
||||
|
||||
[JsonProperty("conflictKeyBindingActionId")]
|
||||
public List<int> ConflictKeyBindingActionId { get; set; } // 冲突的按键绑定动作ID
|
||||
|
||||
[JsonProperty("lastSeenPreDownloadTime")]
|
||||
public long LastSeenPreDownloadTime { get; set; } // 上次看到的预下载时间
|
||||
|
||||
[JsonProperty("lastSeenSettingResourceTabScriptVersion")]
|
||||
public string LastSeenSettingResourceTabScriptVersion { get; set; } // 上次看到的设置资源标签脚本版本
|
||||
|
||||
[JsonProperty("enableEffectAssembleInEditor")]
|
||||
public bool EnableEffectAssembleInEditor { get; set; } // 启用在编辑器中组装效果
|
||||
|
||||
[JsonProperty("forceDisableQuestResourceManagement")]
|
||||
public bool ForceDisableQuestResourceManagement { get; set; } // 强制禁用任务资源管理
|
||||
|
||||
[JsonProperty("needReportQuestResourceDeleteStatusFiles")]
|
||||
public bool NeedReportQuestResourceDeleteStatusFiles { get; set; } // 需要报告任务资源删除状态文件
|
||||
|
||||
[JsonProperty("disableTeamPageBackgroundSwitch")]
|
||||
public bool DisableTeamPageBackgroundSwitch { get; set; } // 禁用团队页面背景切换
|
||||
|
||||
[JsonProperty("disableHttpDns")]
|
||||
public bool DisableHttpDns { get; set; } // 禁用HTTP DNS
|
||||
|
||||
[JsonProperty("mtrCached")]
|
||||
public bool MtrCached { get; set; } // MTR缓存
|
||||
|
||||
[JsonProperty("mtrIsOpen")]
|
||||
public bool MtrIsOpen { get; set; } // MTR是否开启
|
||||
|
||||
[JsonProperty("mtrMaxTTL")]
|
||||
public int MtrMaxTTL { get; set; } // MTR最大TTL
|
||||
|
||||
[JsonProperty("mtrTimeOut")]
|
||||
public int MtrTimeOut { get; set; } // MTR超时时间
|
||||
|
||||
[JsonProperty("mtrTraceCount")]
|
||||
public int MtrTraceCount { get; set; } // MTR跟踪次数
|
||||
|
||||
[JsonProperty("mtrAbortTimeOutCount")]
|
||||
public int MtrAbortTimeOutCount { get; set; } // MTR中止超时计数
|
||||
|
||||
[JsonProperty("mtrAutoTraceInterval")]
|
||||
public int MtrAutoTraceInterval { get; set; } // MTR自动跟踪间隔
|
||||
|
||||
[JsonProperty("mtrTraceCDEachReason")]
|
||||
public int MtrTraceCDEachReason { get; set; } // MTR每个原因的冷却时间
|
||||
|
||||
[JsonProperty("mtrTimeInterval")]
|
||||
public int MtrTimeInterval { get; set; } // MTR时间间隔
|
||||
|
||||
[JsonProperty("mtrBanReasons")]
|
||||
public List<string> MtrBanReasons { get; set; } // MTR禁止原因
|
||||
|
||||
[JsonProperty("_customDataKeyList")]
|
||||
public List<string> CustomDataKeyList { get; set; } // 自定义数据键列表
|
||||
|
||||
[JsonProperty("_customDataValueList")]
|
||||
public List<string> CustomDataValueList { get; set; } // 自定义数据值列表
|
||||
|
||||
[JsonProperty("_serializedCodeSwitches")]
|
||||
public List<int> SerializedCodeSwitches { get; set; } // 序列化代码开关
|
||||
|
||||
[JsonProperty("urlCheckCached")]
|
||||
public bool UrlCheckCached { get; set; } // URL检查缓存
|
||||
|
||||
[JsonProperty("urlCheckIsOpen")]
|
||||
public bool UrlCheckIsOpen { get; set; } // URL检查是否开启
|
||||
|
||||
[JsonProperty("urlCheckAllIP")]
|
||||
public bool UrlCheckAllIP { get; set; } // URL检查所有IP
|
||||
|
||||
[JsonProperty("urlCheckTimeOut")]
|
||||
public int UrlCheckTimeOut { get; set; } // URL检查超时时间
|
||||
|
||||
[JsonProperty("urlCheckSueecssTraceCount")]
|
||||
public int UrlCheckSuccessTraceCount { get; set; } // URL检查成功跟踪计数
|
||||
|
||||
[JsonProperty("urlCheckErrorTraceCount")]
|
||||
public int UrlCheckErrorTraceCount { get; set; } // URL检查错误跟踪计数
|
||||
|
||||
[JsonProperty("urlCheckAbortTimeOutCount")]
|
||||
public int UrlCheckAbortTimeOutCount { get; set; } // URL检查中止超时计数
|
||||
|
||||
[JsonProperty("urlCheckTimeInterval")]
|
||||
public int UrlCheckTimeInterval { get; set; } // URL检查时间间隔
|
||||
|
||||
[JsonProperty("urlCheckCDEachReason")]
|
||||
public int UrlCheckCDEachReason { get; set; } // URL检查每个原因的冷却时间
|
||||
|
||||
[JsonProperty("urlCheckBanReasons")]
|
||||
public List<string> UrlCheckBanReasons { get; set; } // URL检查禁止原因
|
||||
|
||||
[JsonProperty("mtrUseOldWinVersion")]
|
||||
public bool MtrUseOldWinVersion { get; set; } // 使用旧版Windows的MTR
|
||||
|
||||
[JsonProperty("greyTestDeviceUniqueId")]
|
||||
public string GreyTestDeviceUniqueId { get; set; } // 灰度测试设备唯一ID
|
||||
|
||||
[JsonProperty("muteAudioOnAppMinimized")]
|
||||
public bool MuteAudioOnAppMinimized { get; set; } // 应用最小化时静音
|
||||
|
||||
[JsonProperty("disableFallbackControllerType")]
|
||||
public bool DisableFallbackControllerType { get; set; } // 禁用回退控制器类型
|
||||
|
||||
[JsonProperty("lastShowDoorProgress")]
|
||||
public float LastShowDoorProgress { get; set; } // 上次显示门进度
|
||||
|
||||
[JsonProperty("globalPerfSettingVersion")]
|
||||
public int GlobalPerfSettingVersion { get; set; } // 全局性能设置版本
|
||||
|
||||
|
||||
public static string? GetStrFromRegistry()
|
||||
{
|
||||
if (GenshinRegistry.GetRegistryKey() is not { } hk)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (hk)
|
||||
{
|
||||
string value_name = SearchRegistryName(hk);
|
||||
if (hk.GetValue(value_name) is not byte[] rawBytes)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var str = Encoding.UTF8.GetString(rawBytes);
|
||||
// Debug.WriteLine(str);
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
public static GenshinGameSettings? Parse(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<GenshinGameSettings>(json);
|
||||
}
|
||||
|
||||
|
||||
private static string SearchRegistryName(RegistryKey key)
|
||||
{
|
||||
string value_name = string.Empty;
|
||||
string[] names = key.GetValueNames();
|
||||
|
||||
foreach (string name in names)
|
||||
{
|
||||
if (name.Contains("GENERAL_DATA"))
|
||||
{
|
||||
value_name = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (value_name == string.Empty)
|
||||
{
|
||||
throw new ArgumentException(value_name);
|
||||
}
|
||||
|
||||
return value_name;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BetterGenshinImpact.Helpers.Http;
|
||||
|
||||
@@ -15,9 +16,6 @@ public class ProxySpeedTester
|
||||
"{0}",
|
||||
"https://mirror.ghproxy.com/{0}",
|
||||
"https://hub.gitmirror.com/{0}",
|
||||
"https://ghproxy.cc/{0}",
|
||||
"https://www.ghproxy.cc/{0}",
|
||||
"https://ghproxy.cn/{0}",
|
||||
"https://ghproxy.net/{0}"
|
||||
];
|
||||
|
||||
@@ -70,11 +68,13 @@ public class ProxySpeedTester
|
||||
// 模拟代理测试请求
|
||||
var response = await _httpClient.GetAsync(string.Format(proxyAddress, target), cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return (proxyAddress, await response.Content.ReadAsStringAsync(cancellationToken), true);
|
||||
var content = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
var json = JObject.Parse(content);
|
||||
return (proxyAddress, content, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return (proxyAddress, e.Message, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
BetterGenshinImpact/Helpers/SecurityControlHelper.cs
Normal file
36
BetterGenshinImpact/Helpers/SecurityControlHelper.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.AccessControl;
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace BetterGenshinImpact.Helpers;
|
||||
|
||||
public static class SecurityControlHelper
|
||||
{
|
||||
|
||||
public static void AllowFullFolderSecurity(string dirPath)
|
||||
{
|
||||
if (!RuntimeHelper.IsElevated)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DirectoryInfo dir = new(dirPath);
|
||||
DirectorySecurity dirSecurity = dir.GetAccessControl(AccessControlSections.All);
|
||||
InheritanceFlags inherits = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
|
||||
FileSystemAccessRule everyoneFileSystemAccessRule = new("Everyone", FileSystemRights.FullControl, inherits, PropagationFlags.None, AccessControlType.Allow);
|
||||
FileSystemAccessRule usersFileSystemAccessRule = new("Users", FileSystemRights.FullControl, inherits, PropagationFlags.None, AccessControlType.Allow);
|
||||
dirSecurity.ModifyAccessRule(AccessControlModification.Add, everyoneFileSystemAccessRule, out _);
|
||||
dirSecurity.ModifyAccessRule(AccessControlModification.Add, usersFileSystemAccessRule, out _);
|
||||
dir.SetAccessControl(dirSecurity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TaskControl.Logger.LogError("首次运行自动初始化按键绑定异常:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
|
||||
MessageBox.Show("检测到当前 BetterGI 位于C盘,尝试修改目录权限失败,可能会导致WebView2相关的功能无法使用!" + e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
BetterGenshinImpact/Helpers/TempManager.cs
Normal file
27
BetterGenshinImpact/Helpers/TempManager.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
|
||||
namespace BetterGenshinImpact.Helpers;
|
||||
|
||||
public class TempManager
|
||||
{
|
||||
public static string GetTempDirectory()
|
||||
{
|
||||
var tmp = Global.Absolute("User/Temp");
|
||||
Directory.CreateDirectory(tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public static void CleanUp()
|
||||
{
|
||||
try
|
||||
{
|
||||
DirectoryHelper.DeleteDirectoryRecursively(GetTempDirectory());
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Suppress any exceptions to avoid exposing errors
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
using BetterGenshinImpact.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using BetterGenshinImpact.Model;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
@@ -54,4 +57,103 @@ public class FileTreeNodeHelper
|
||||
parentNode.Children.Add(fileNode);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据路径过滤树形结构,排除指定路径的节点
|
||||
/// </summary>
|
||||
/// <typeparam name="T">节点数据类型</typeparam>
|
||||
/// <param name="nodes">树形结构的根节点集合</param>
|
||||
/// <param name="folderNames">要排除的路径集合</param>
|
||||
/// <returns>过滤后的树形结构</returns>
|
||||
public static ObservableCollection<FileTreeNode<T>> FilterTree<T>(
|
||||
ObservableCollection<FileTreeNode<T>> nodes,
|
||||
List<string> folderNames)
|
||||
{
|
||||
// 递归过滤节点
|
||||
ObservableCollection<FileTreeNode<T>> FilterNodes(ObservableCollection<FileTreeNode<T>> inputNodes, List<string> paths)
|
||||
{
|
||||
var filteredNodes = new ObservableCollection<FileTreeNode<T>>();
|
||||
|
||||
foreach (var node in inputNodes)
|
||||
{
|
||||
// 获取当前层需要排除的路径
|
||||
var matchedPaths = paths
|
||||
.Where(path => IsPathMatch(node.FileName ??"", path))
|
||||
.Select(path => GetRemainingPath(node.FileName ?? "" , path))
|
||||
.Where(remainingPath => remainingPath != null)
|
||||
.ToList();
|
||||
|
||||
// 如果当前路径完全匹配,跳过当前节点
|
||||
if (matchedPaths.Any(path => path == ""))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 递归对子节点过滤
|
||||
node.Children = FilterNodes(node.Children, matchedPaths);
|
||||
|
||||
// 添加过滤后的节点
|
||||
filteredNodes.Add(node);
|
||||
}
|
||||
|
||||
return filteredNodes;
|
||||
}
|
||||
|
||||
return FilterNodes(nodes, folderNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断文件路径是否匹配多级路径的前缀
|
||||
/// </summary>
|
||||
/// <param name="fileName">当前节点路径</param>
|
||||
/// <param name="path">目标路径</param>
|
||||
/// <returns>是否匹配</returns>
|
||||
private static bool IsPathMatch(string fileName, string path)
|
||||
{
|
||||
// 匹配路径是否以指定前缀开始,路径分隔符对齐
|
||||
return path.StartsWith(fileName, StringComparison.OrdinalIgnoreCase)
|
||||
&& (path.Length == fileName.Length || path[fileName.Length] == '\\');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取路径中去掉当前节点后的剩余路径
|
||||
/// </summary>
|
||||
/// <param name="fileName">当前节点路径</param>
|
||||
/// <param name="path">完整路径</param>
|
||||
/// <returns>剩余路径,如果不匹配返回 null</returns>
|
||||
private static string? GetRemainingPath(string fileName, string path)
|
||||
{
|
||||
if (IsPathMatch(fileName, path))
|
||||
{
|
||||
return path.Length > fileName.Length ? path.Substring(fileName.Length + 1) : "";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static ObservableCollection<FileTreeNode<T>> FilterEmptyNodes<T>(ObservableCollection<FileTreeNode<T>> nodes)
|
||||
{
|
||||
// 递归过滤节点
|
||||
ObservableCollection<FileTreeNode<T>> Filter(ObservableCollection<FileTreeNode<T>> inputNodes)
|
||||
{
|
||||
var filteredNodes = new ObservableCollection<FileTreeNode<T>>();
|
||||
|
||||
foreach (var node in inputNodes)
|
||||
{
|
||||
// 递归处理子节点
|
||||
node.Children = Filter(node.Children);
|
||||
|
||||
// 如果是目录并且没有子节点,跳过当前节点
|
||||
if (node.IsDirectory && !node.Children.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 其他情况保留节点
|
||||
filteredNodes.Add(node);
|
||||
}
|
||||
|
||||
return filteredNodes;
|
||||
}
|
||||
|
||||
return Filter(nodes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Builder;
|
||||
|
||||
public interface INotificationDataBuilder<TNotificationData> where TNotificationData : INotificationData
|
||||
{
|
||||
TNotificationData Build();
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Builder;
|
||||
|
||||
public class LifecycleNotificationBuilder : INotificationDataBuilder<LifecycleNotificationData>
|
||||
{
|
||||
private readonly LifecycleNotificationData _notificationData = new();
|
||||
|
||||
public LifecycleNotificationData Build()
|
||||
{
|
||||
return _notificationData;
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Builder;
|
||||
|
||||
public class TaskNotificationBuilder : INotificationDataBuilder<TaskNotificationData>
|
||||
{
|
||||
private readonly TaskNotificationData _notificationData = new();
|
||||
|
||||
public TaskNotificationBuilder WithEvent(NotificationEvent notificationEvent)
|
||||
{
|
||||
_notificationData.Event = notificationEvent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder WithAction(NotificationAction notificationAction)
|
||||
{
|
||||
_notificationData.Action = notificationAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder WithConclusion(NotificationConclusion? conclusion)
|
||||
{
|
||||
_notificationData.Conclusion = conclusion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder GeniusInvocation()
|
||||
{
|
||||
return WithEvent(NotificationEvent.GeniusInvocation);
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder Domain()
|
||||
{
|
||||
return WithEvent(NotificationEvent.Domain);
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder Started()
|
||||
{
|
||||
return WithAction(NotificationAction.Started);
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder Completed()
|
||||
{
|
||||
return WithAction(NotificationAction.Completed);
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder Progress()
|
||||
{
|
||||
return WithAction(NotificationAction.Progress);
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder Success()
|
||||
{
|
||||
return WithAction(NotificationAction.Completed)
|
||||
.WithConclusion(NotificationConclusion.Success);
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder Failure()
|
||||
{
|
||||
return WithAction(NotificationAction.Completed)
|
||||
.WithConclusion(NotificationConclusion.Failure);
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder Cancelled()
|
||||
{
|
||||
return WithAction(NotificationAction.Completed)
|
||||
.WithConclusion(NotificationConclusion.Cancelled);
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder WithScreenshot(Image? screenshot)
|
||||
{
|
||||
_notificationData.Screenshot = screenshot;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskNotificationBuilder AddTask(object task)
|
||||
{
|
||||
_notificationData.Task = task;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskNotificationData Build()
|
||||
{
|
||||
return _notificationData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Converter;
|
||||
|
||||
public class BaseDateTimeJsonConverter(string format) : JsonConverter<DateTime>
|
||||
{
|
||||
|
||||
// 反序列化方法
|
||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.String)
|
||||
{
|
||||
if (DateTime.TryParseExact(reader.GetString(), format, null, System.Globalization.DateTimeStyles.None, out DateTime date))
|
||||
{
|
||||
return date;
|
||||
}
|
||||
}
|
||||
return reader.GetDateTime();
|
||||
}
|
||||
|
||||
// 序列化方法
|
||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteStringValue(value.ToString(format));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Converter;
|
||||
|
||||
public class DateTimeJsonConverter() : BaseDateTimeJsonConverter("yyyy-MM-dd HH:mm:ss")
|
||||
{
|
||||
}
|
||||
@@ -11,11 +11,11 @@ public class ImageToBase64Converter : JsonConverter<Image>
|
||||
{
|
||||
public override Image? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.GetString() is string base64)
|
||||
if (reader.GetString() is { } base64)
|
||||
{
|
||||
return Image.FromStream(new MemoryStream(Convert.FromBase64String(base64)));
|
||||
}
|
||||
return default;
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, Image value, JsonSerializerOptions options)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace BetterGenshinImpact.Service.Notification.Model.Base;
|
||||
|
||||
// TODO: 需要制定标准 (currently not referenced)
|
||||
public class DomainDetails
|
||||
{
|
||||
public string DomainName { get; set; } = string.Empty;
|
||||
public int RunCount { get; set; }
|
||||
public string RewardStatus { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace BetterGenshinImpact.Service.Notification.Model.Base;
|
||||
|
||||
// TODO: 需要制定标准 (currently not referenced)
|
||||
public class GeniusInvocationDetails
|
||||
{
|
||||
public string GameState { get; set; } = string.Empty;
|
||||
public int RoundNumber { get; set; }
|
||||
public string OpponentName { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace BetterGenshinImpact.Service.Notification.Model.Base;
|
||||
|
||||
// TODO: 需要制定标准 (currently not referenced)
|
||||
public class ScriptDetails
|
||||
{
|
||||
public string ScriptName { get; set; } = string.Empty;
|
||||
public string ScriptPath { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Drawing;
|
||||
using BetterGenshinImpact.GameTask;
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using BetterGenshinImpact.Service.Notification.Converter;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
public class BaseNotificationData
|
||||
{
|
||||
/// <summary>
|
||||
/// NotificationEvent
|
||||
/// 事件名称
|
||||
/// </summary>
|
||||
public string Event { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 事件结果
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public NotificationEventResult Result { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 事件触发时间
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(DateTimeJsonConverter))]
|
||||
public DateTime Timestamp { get; set; } = DateTime.Now;
|
||||
|
||||
/// <summary>
|
||||
/// 事件触发时的截图
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(ImageToBase64Converter))]
|
||||
public Image? Screenshot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 事件消息
|
||||
/// </summary>
|
||||
public string? Message { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 额外的事件数据
|
||||
/// </summary>
|
||||
public object? Data { get; set; }
|
||||
|
||||
public void Send()
|
||||
{
|
||||
if (TaskContext.Instance().Config.NotificationConfig.IncludeScreenShot)
|
||||
{
|
||||
Screenshot = (Bitmap)TaskControl.CaptureToRectArea().SrcBitmap.Clone();
|
||||
}
|
||||
|
||||
NotificationService.Instance().NotifyAllNotifiers(this);
|
||||
}
|
||||
|
||||
public void Send(string message)
|
||||
{
|
||||
Message = message;
|
||||
Send();
|
||||
}
|
||||
|
||||
public void Success(string message)
|
||||
{
|
||||
Message = message;
|
||||
Result = NotificationEventResult.Success;
|
||||
Send();
|
||||
}
|
||||
|
||||
public void Fail(string message)
|
||||
{
|
||||
Message = message;
|
||||
Result = NotificationEventResult.Fail;
|
||||
Send();
|
||||
}
|
||||
|
||||
public void Error(string message)
|
||||
{
|
||||
Message = message;
|
||||
Result = NotificationEventResult.Fail;
|
||||
Send();
|
||||
}
|
||||
|
||||
public void Error(string message, Exception exception)
|
||||
{
|
||||
Message = message + Environment.NewLine + exception.Message + Environment.NewLine + exception.StackTrace;
|
||||
Result = NotificationEventResult.Fail;
|
||||
Send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using BetterGenshinImpact.GameTask.AutoDomain;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Base;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
public class DomainNotificationData : BaseNotificationData
|
||||
{
|
||||
public AutoDomainParam? Domain { get; set; }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
|
||||
public enum NotificationAction
|
||||
{
|
||||
Started,
|
||||
Completed,
|
||||
Progress
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
|
||||
public enum NotificationConclusion
|
||||
{
|
||||
Success,
|
||||
Failure,
|
||||
Cancelled
|
||||
}
|
||||
@@ -1,8 +1,24 @@
|
||||
namespace BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
|
||||
public enum NotificationEvent
|
||||
public class NotificationEvent(string code, string msg)
|
||||
{
|
||||
Test,
|
||||
GeniusInvocation,
|
||||
Domain
|
||||
public static readonly NotificationEvent Test = new("notify.test", "测试通知");
|
||||
public static readonly NotificationEvent DomainReward = new("domain.reward", "自动秘境奖励");
|
||||
public static readonly NotificationEvent DomainStart = new("domain.start", "自动秘境启动");
|
||||
public static readonly NotificationEvent DomainEnd = new("domain.end", "自动秘境结束");
|
||||
public static readonly NotificationEvent DomainRetry = new("domain.retry", "自动秘境重试");
|
||||
public static readonly NotificationEvent TaskCancel = new("task.cancel", "任务启动");
|
||||
public static readonly NotificationEvent TaskError = new("task.error", "任务错误");
|
||||
public static readonly NotificationEvent GroupStart = new("group.start", "配置组启动");
|
||||
public static readonly NotificationEvent GroupEnd = new("group.end", "配置组结束");
|
||||
public static readonly NotificationEvent DragonStart = new("dragon.start", "一条龙启动");
|
||||
public static readonly NotificationEvent DragonEnd = new("dragon.end", "一条龙结束");
|
||||
public static readonly NotificationEvent TcgStart = new("tcg.start", "七圣召唤启动");
|
||||
public static readonly NotificationEvent TcgEnd = new("tcg.end", "七圣召唤结束");
|
||||
public static readonly NotificationEvent AlbumStart = new("album.start", "自动音游专辑启动");
|
||||
public static readonly NotificationEvent AlbumEnd = new("album.end", "自动音游专辑结束");
|
||||
public static readonly NotificationEvent AlbumError = new("album.error", "自动音游专辑错误");
|
||||
|
||||
public string Code { get; private set; } = code;
|
||||
public string Msg { get; private set; } = msg;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
|
||||
public enum NotificationEventResult
|
||||
{
|
||||
Success,
|
||||
Fail,
|
||||
PartialSuccess, // 还没想好怎么用,先放在这里
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Model;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Base;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
public class GeniusInvocationNotificationData : BaseNotificationData
|
||||
{
|
||||
public Duel? GeniusInvocation { get; set; }
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
public interface INotificationData
|
||||
{
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public NotificationEvent Event { get; set; }
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
public record LifecycleNotificationData : INotificationData
|
||||
{
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public NotificationEvent Event { get; set; }
|
||||
|
||||
public static LifecycleNotificationData Test()
|
||||
{
|
||||
return new LifecycleNotificationData() { Event = NotificationEvent.Test };
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ public class NotificationTestResult
|
||||
|
||||
public static NotificationTestResult Success()
|
||||
{
|
||||
return new NotificationTestResult { IsSuccess = true, Message = "成功" };
|
||||
return new NotificationTestResult { IsSuccess = true, Message = "通知成功" };
|
||||
}
|
||||
|
||||
public static NotificationTestResult Error(string message)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using BetterGenshinImpact.Core.Script.Group;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Base;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
public class ScriptNotificationData : BaseNotificationData
|
||||
{
|
||||
public ScriptGroupProject? Script { get; set; }
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using BetterGenshinImpact.Service.Notification.Converter;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using System.Drawing;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
public record TaskNotificationData : INotificationData
|
||||
{
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public NotificationEvent Event { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public NotificationAction Action { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public NotificationConclusion? Conclusion { get; set; }
|
||||
|
||||
[JsonConverter(typeof(ImageToBase64Converter))]
|
||||
public Image? Screenshot { get; set; }
|
||||
|
||||
public object? Task { get; set; }
|
||||
}
|
||||
@@ -9,6 +9,11 @@ namespace BetterGenshinImpact.Service.Notification;
|
||||
[Serializable]
|
||||
public partial class NotificationConfig : ObservableObject
|
||||
{
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
private string _notificationEventSubscribe = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@@ -20,4 +25,48 @@ public partial class NotificationConfig : ObservableObject
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private string _webhookEndpoint = string.Empty;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否包含截图
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _includeScreenShot = true;
|
||||
|
||||
/// <summary>
|
||||
/// windows uwp 通知是否启用
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _windowsUwpNotificationEnabled = false;
|
||||
|
||||
|
||||
// 飞书通知
|
||||
/// <summary>
|
||||
/// 飞书通知是否启用
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _feishuNotificationEnabled = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 飞书通知地址
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private string _feishuWebhookUrl = string.Empty;
|
||||
|
||||
|
||||
// 企业微信通知
|
||||
/// <summary>
|
||||
/// 企业微信通知是否启用
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool _workweixinNotificationEnabled = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 企业微信通知通知地址
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private string _workweixinWebhookUrl = string.Empty;
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using BetterGenshinImpact.Service.Notification.Builder;
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification;
|
||||
|
||||
public class NotificationHelper
|
||||
{
|
||||
public static void Notify(INotificationData notificationData)
|
||||
{
|
||||
NotificationService.Instance().NotifyAllNotifiers(notificationData);
|
||||
}
|
||||
|
||||
public static void SendTaskNotificationUsing(Func<TaskNotificationBuilder, INotificationData> builderFunc)
|
||||
{
|
||||
var builder = new TaskNotificationBuilder();
|
||||
Notify(builderFunc(builder));
|
||||
}
|
||||
|
||||
public static void SendTaskNotificationWithScreenshotUsing(Func<TaskNotificationBuilder, INotificationData> builderFunc)
|
||||
{
|
||||
var builder = new TaskNotificationBuilder();
|
||||
var screenShot = (Bitmap)TaskControl.CaptureToRectArea().SrcBitmap.Clone();
|
||||
Notify(builderFunc(builder.WithScreenshot(screenShot)));
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using BetterGenshinImpact.Service.Interface;
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
using BetterGenshinImpact.Service.Notifier;
|
||||
using BetterGenshinImpact.Service.Notifier.Exception;
|
||||
using BetterGenshinImpact.Service.Notifier.Interface;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.GameTask;
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification;
|
||||
|
||||
@@ -18,13 +17,11 @@ public class NotificationService : IHostedService
|
||||
{
|
||||
private static NotificationService? _instance;
|
||||
|
||||
private static readonly HttpClient _httpClient = new();
|
||||
private static readonly HttpClient NotifyHttpClient = new();
|
||||
private readonly NotifierManager _notifierManager;
|
||||
private AllConfig Config { get; set; }
|
||||
|
||||
public NotificationService(IConfigService configService, NotifierManager notifierManager)
|
||||
public NotificationService(NotifierManager notifierManager)
|
||||
{
|
||||
Config = configService.Get();
|
||||
_notifierManager = notifierManager;
|
||||
_instance = this;
|
||||
InitializeNotifiers();
|
||||
@@ -36,6 +33,7 @@ public class NotificationService : IHostedService
|
||||
{
|
||||
throw new Exception("Not instantiated");
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
@@ -49,22 +47,27 @@ public class NotificationService : IHostedService
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private StringContent TransformData(INotificationData notificationData)
|
||||
{
|
||||
// using object type here so it serializes the interface correctly
|
||||
var serializedData = JsonSerializer.Serialize<object>(notificationData, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
|
||||
return new StringContent(serializedData, Encoding.UTF8, "application/json");
|
||||
}
|
||||
|
||||
private void InitializeNotifiers()
|
||||
{
|
||||
if (Config.NotificationConfig.WebhookEnabled)
|
||||
if (TaskContext.Instance().Config.NotificationConfig.WebhookEnabled)
|
||||
{
|
||||
_notifierManager.RegisterNotifier(new WebhookNotifier(_httpClient, Config.NotificationConfig.WebhookEndpoint));
|
||||
_notifierManager.RegisterNotifier(new WebhookNotifier(NotifyHttpClient, TaskContext.Instance().Config.NotificationConfig.WebhookEndpoint));
|
||||
}
|
||||
|
||||
if (TaskContext.Instance().Config.NotificationConfig.WindowsUwpNotificationEnabled)
|
||||
{
|
||||
_notifierManager.RegisterNotifier(new WindowsUwpNotifier());
|
||||
}
|
||||
|
||||
if (TaskContext.Instance().Config.NotificationConfig.FeishuNotificationEnabled)
|
||||
{
|
||||
_notifierManager.RegisterNotifier(new FeishuNotifier(NotifyHttpClient, TaskContext.Instance().Config.NotificationConfig.FeishuWebhookUrl));
|
||||
}
|
||||
|
||||
if (TaskContext.Instance().Config.NotificationConfig.WorkweixinNotificationEnabled)
|
||||
{
|
||||
_notifierManager.RegisterNotifier(new WorkWeixinNotifier(NotifyHttpClient, TaskContext.Instance().Config.NotificationConfig.WorkweixinWebhookUrl));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +86,19 @@ public class NotificationService : IHostedService
|
||||
{
|
||||
return NotificationTestResult.Error("通知类型未启用");
|
||||
}
|
||||
await notifier.SendNotificationAsync(TransformData(LifecycleNotificationData.Test()));
|
||||
|
||||
var testData = new BaseNotificationData
|
||||
{
|
||||
Event = NotificationEvent.Test.Code,
|
||||
Result = NotificationEventResult.Success,
|
||||
Message = "这是一条测试通知信息",
|
||||
};
|
||||
if (TaskContext.Instance().IsInitialized)
|
||||
{
|
||||
testData.Screenshot = TaskControl.CaptureToRectArea().SrcBitmap;
|
||||
}
|
||||
|
||||
await notifier.SendAsync(testData);
|
||||
return NotificationTestResult.Success();
|
||||
}
|
||||
catch (NotifierException ex)
|
||||
@@ -92,13 +107,22 @@ public class NotificationService : IHostedService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task NotifyAllNotifiersAsync(INotificationData notificationData)
|
||||
public async Task NotifyAllNotifiersAsync(BaseNotificationData notificationData)
|
||||
{
|
||||
await _notifierManager.SendNotificationToAllAsync(TransformData(notificationData));
|
||||
var subscribeEventStr = TaskContext.Instance().Config.NotificationConfig.NotificationEventSubscribe;
|
||||
if (!string.IsNullOrEmpty(subscribeEventStr))
|
||||
{
|
||||
if (!subscribeEventStr.Contains(notificationData.Event))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await _notifierManager.SendNotificationToAllAsync(notificationData);
|
||||
}
|
||||
|
||||
public void NotifyAllNotifiers(INotificationData notificationData)
|
||||
public void NotifyAllNotifiers(BaseNotificationData notificationData)
|
||||
{
|
||||
Task.Run(() => NotifyAllNotifiersAsync(notificationData));
|
||||
}
|
||||
}
|
||||
}
|
||||
23
BetterGenshinImpact/Service/Notification/Notify.cs
Normal file
23
BetterGenshinImpact/Service/Notification/Notify.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notification;
|
||||
|
||||
public class Notify
|
||||
{
|
||||
public static BaseNotificationData Event(string eventName)
|
||||
{
|
||||
return new BaseNotificationData
|
||||
{
|
||||
Event = eventName
|
||||
};
|
||||
}
|
||||
|
||||
public static BaseNotificationData Event(NotificationEvent eventEnum)
|
||||
{
|
||||
return new BaseNotificationData
|
||||
{
|
||||
Event = eventEnum.Code
|
||||
};
|
||||
}
|
||||
}
|
||||
66
BetterGenshinImpact/Service/Notifier/FeishuNotifier.cs
Normal file
66
BetterGenshinImpact/Service/Notifier/FeishuNotifier.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using BetterGenshinImpact.Service.Notifier.Exception;
|
||||
using BetterGenshinImpact.Service.Notifier.Interface;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notifier;
|
||||
|
||||
public class FeishuNotifier : INotifier
|
||||
{
|
||||
public string Name { get; set; } = "Feishu";
|
||||
|
||||
public string Endpoint { get; set; }
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public FeishuNotifier(HttpClient httpClient, string endpoint = "")
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
Endpoint = endpoint;
|
||||
}
|
||||
|
||||
public async Task SendAsync(BaseNotificationData content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Endpoint))
|
||||
{
|
||||
throw new NotifierException("Feishu webhook endpoint is not set");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.PostAsync(Endpoint, TransformData(content));
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new NotifierException($"Feishu webhook call failed with code: {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
catch (NotifierException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
throw new NotifierException($"Error sending Feishu webhook: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private StringContent TransformData(BaseNotificationData notificationData)
|
||||
{
|
||||
var feishuMessage = new
|
||||
{
|
||||
msg_type = "text",
|
||||
content = new
|
||||
{
|
||||
text = notificationData.Message
|
||||
}
|
||||
};
|
||||
|
||||
var serializedData = JsonSerializer.Serialize(feishuMessage);
|
||||
|
||||
return new StringContent(serializedData, Encoding.UTF8, "application/json");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notifier.Interface;
|
||||
|
||||
@@ -7,6 +8,5 @@ public interface INotifier
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
// TODO: replace HttpContent with another data structure
|
||||
Task SendNotificationAsync(HttpContent content);
|
||||
Task SendAsync(BaseNotificationData data);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using BetterGenshinImpact.Service.Notifier.Interface;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notifier;
|
||||
|
||||
@@ -38,31 +37,30 @@ public class NotifierManager
|
||||
return _notifiers.FirstOrDefault(o => o is T);
|
||||
}
|
||||
|
||||
public async Task SendNotificationAsync(INotifier notifier, HttpContent httpContent)
|
||||
public async Task SendNotificationAsync(INotifier notifier, BaseNotificationData content)
|
||||
{
|
||||
try
|
||||
{
|
||||
await notifier.SendNotificationAsync(httpContent);
|
||||
await notifier.SendAsync(content);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Logger.LogError("{name} 通知发送失败", notifier.Name);
|
||||
Debug.WriteLine(ex);
|
||||
Logger.LogWarning("{name} 通知发送失败: {ex}", notifier.Name, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendNotificationAsync<T>(HttpContent httpContent) where T : INotifier
|
||||
public async Task SendNotificationAsync<T>(BaseNotificationData content) where T : INotifier
|
||||
{
|
||||
var notifier = _notifiers.FirstOrDefault(o => o is T);
|
||||
|
||||
if (notifier != null)
|
||||
{
|
||||
await SendNotificationAsync(notifier, httpContent);
|
||||
await SendNotificationAsync(notifier, content);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendNotificationToAllAsync(HttpContent httpContent)
|
||||
public async Task SendNotificationToAllAsync(BaseNotificationData content)
|
||||
{
|
||||
await Task.WhenAll(_notifiers.Select(notifier => SendNotificationAsync(notifier, httpContent)));
|
||||
await Task.WhenAll(_notifiers.Select(notifier => SendNotificationAsync(notifier, content)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using BetterGenshinImpact.Service.Notifier.Exception;
|
||||
using BetterGenshinImpact.Service.Notifier.Interface;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notifier;
|
||||
|
||||
@@ -12,6 +15,11 @@ public class WebhookNotifier : INotifier
|
||||
public string Endpoint { get; set; }
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||
};
|
||||
|
||||
public WebhookNotifier(HttpClient httpClient, string endpoint = "")
|
||||
{
|
||||
@@ -19,11 +27,16 @@ public class WebhookNotifier : INotifier
|
||||
Endpoint = endpoint;
|
||||
}
|
||||
|
||||
public async Task SendNotificationAsync(HttpContent content)
|
||||
public async Task SendAsync(BaseNotificationData content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Endpoint))
|
||||
{
|
||||
throw new NotifierException("Webhook 地址为空");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.PostAsync(Endpoint, content);
|
||||
var response = await _httpClient.PostAsync(Endpoint, TransformData(content));
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
@@ -39,4 +52,13 @@ public class WebhookNotifier : INotifier
|
||||
throw new NotifierException($"Error sending webhook: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private StringContent TransformData(BaseNotificationData notificationData)
|
||||
{
|
||||
// using object type here so it serializes the interface correctly
|
||||
var serializedData = JsonSerializer.Serialize<object>(notificationData, _jsonSerializerOptions);
|
||||
|
||||
return new StringContent(serializedData, Encoding.UTF8, "application/json");
|
||||
}
|
||||
}
|
||||
39
BetterGenshinImpact/Service/Notifier/WindowsUwpNotifier.cs
Normal file
39
BetterGenshinImpact/Service/Notifier/WindowsUwpNotifier.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Helpers;
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
using BetterGenshinImpact.Service.Notifier.Interface;
|
||||
using Microsoft.Toolkit.Uwp.Notifications;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notifier;
|
||||
|
||||
public class WindowsUwpNotifier : INotifier
|
||||
{
|
||||
public string Name => "Windows通知";
|
||||
|
||||
public Task SendAsync(BaseNotificationData data)
|
||||
{
|
||||
var toastBuilder = new ToastContentBuilder();
|
||||
|
||||
if (data.Screenshot != null)
|
||||
{
|
||||
string uniqueFileName = $"notification_image_{Guid.NewGuid()}.png";
|
||||
string imagePath = Path.Combine(TempManager.GetTempDirectory(), uniqueFileName);
|
||||
data.Screenshot.Save(imagePath, System.Drawing.Imaging.ImageFormat.Png);
|
||||
toastBuilder.AddHeroImage(new Uri(imagePath));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(data.Message))
|
||||
{
|
||||
toastBuilder.AddText(data.Message);
|
||||
}
|
||||
|
||||
toastBuilder.Show(toast =>
|
||||
{
|
||||
toast.Group = data.Event.ToString();
|
||||
toast.ExpirationTime = DateTime.Now.AddHours(12);
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
66
BetterGenshinImpact/Service/Notifier/WorkWeixinNotifier.cs
Normal file
66
BetterGenshinImpact/Service/Notifier/WorkWeixinNotifier.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using BetterGenshinImpact.Service.Notifier.Exception;
|
||||
using BetterGenshinImpact.Service.Notifier.Interface;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Service.Notification.Model;
|
||||
|
||||
namespace BetterGenshinImpact.Service.Notifier;
|
||||
|
||||
public class WorkWeixinNotifier : INotifier
|
||||
{
|
||||
public string Name { get; set; } = "WorkWeixin";
|
||||
|
||||
public string Endpoint { get; set; }
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public WorkWeixinNotifier(HttpClient httpClient, string endpoint = "")
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
Endpoint = endpoint;
|
||||
}
|
||||
|
||||
public async Task SendAsync(BaseNotificationData content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Endpoint))
|
||||
{
|
||||
throw new NotifierException("WorkWeixin webhook endpoint is not set");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _httpClient.PostAsync(Endpoint, TransformData(content));
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
throw new NotifierException($"WorkWeixin webhook call failed with code: {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
catch (NotifierException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
throw new NotifierException($"Error sending WorkWeixin webhook: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private StringContent TransformData(BaseNotificationData notificationData)
|
||||
{
|
||||
var workweixinMessage = new
|
||||
{
|
||||
msgtype = "text",
|
||||
text = new
|
||||
{
|
||||
content = notificationData.Message
|
||||
}
|
||||
};
|
||||
|
||||
var serializedData = JsonSerializer.Serialize(workweixinMessage);
|
||||
|
||||
return new StringContent(serializedData, Encoding.UTF8, "application/json");
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,32 @@ using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
|
||||
using BetterGenshinImpact.GameTask.Common;
|
||||
using BetterGenshinImpact.GameTask.Common.BgiVision;
|
||||
using BetterGenshinImpact.Service.Notification;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
|
||||
namespace BetterGenshinImpact.Service;
|
||||
|
||||
public partial class ScriptService : IScriptService
|
||||
{
|
||||
private readonly ILogger<ScriptService> _logger = App.GetLogger<ScriptService>();
|
||||
private static bool IsCurrentHourEqual(string input)
|
||||
{
|
||||
// 尝试将输入字符串转换为整数
|
||||
if (int.TryParse(input, out int hour))
|
||||
{
|
||||
// 验证小时是否在合法范围内(0-23)
|
||||
if (hour is >= 0 and <= 23)
|
||||
{
|
||||
// 获取当前小时数
|
||||
int currentHour = DateTime.Now.Hour;
|
||||
// 判断是否相等
|
||||
return currentHour == hour;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果输入非数字或不合法,返回 false
|
||||
return false;
|
||||
}
|
||||
public async Task RunMulti(IEnumerable<ScriptGroupProject> projectList, string? groupName = null)
|
||||
{
|
||||
groupName ??= "默认";
|
||||
@@ -51,7 +70,9 @@ public partial class ScriptService : IScriptService
|
||||
}
|
||||
|
||||
var timerOperation = hasTimer ? DispatcherTimerOperationEnum.UseCacheImageWithTriggerEmpty : DispatcherTimerOperationEnum.UseSelfCaptureImage;
|
||||
|
||||
|
||||
Notify.Event(NotificationEvent.GroupStart).Success($"配置组{groupName}启动");
|
||||
|
||||
await new TaskRunner(timerOperation)
|
||||
.RunThreadAsync(async () =>
|
||||
{
|
||||
@@ -59,6 +80,13 @@ public partial class ScriptService : IScriptService
|
||||
|
||||
foreach (var project in list)
|
||||
{
|
||||
|
||||
if (project.GroupInfo is { Config.PathingConfig.Enabled: true } && IsCurrentHourEqual(project.GroupInfo.Config.PathingConfig.SkipDuring))
|
||||
{
|
||||
_logger.LogInformation($"{project.Name}任务已到禁止执行时段,将跳过!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (project.Status != "Enabled")
|
||||
{
|
||||
_logger.LogInformation("脚本 {Name} 状态为禁用,跳过执行", project.Name);
|
||||
@@ -85,6 +113,14 @@ public partial class ScriptService : IScriptService
|
||||
stopwatch.Reset();
|
||||
stopwatch.Start();
|
||||
await ExecuteProject(project);
|
||||
|
||||
//多次执行时及时中断
|
||||
if (project.GroupInfo is { Config.PathingConfig.Enabled: true } && IsCurrentHourEqual(project.GroupInfo.Config.PathingConfig.SkipDuring))
|
||||
{
|
||||
_logger.LogInformation($"{project.Name}任务已到禁止执行时段,将跳过!");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
catch (NormalEndException e)
|
||||
{
|
||||
@@ -104,10 +140,11 @@ public partial class ScriptService : IScriptService
|
||||
{
|
||||
stopwatch.Stop();
|
||||
var elapsedTime = TimeSpan.FromMilliseconds(stopwatch.ElapsedMilliseconds);
|
||||
_logger.LogDebug("→ 脚本执行结束: {Name}, 耗时: {ElapsedMilliseconds} 毫秒", project.Name, stopwatch.ElapsedMilliseconds);
|
||||
// _logger.LogDebug("→ 脚本执行结束: {Name}, 耗时: {ElapsedMilliseconds} 毫秒", project.Name, stopwatch.ElapsedMilliseconds);
|
||||
_logger.LogInformation("→ 脚本执行结束: {Name}, 耗时: {Minutes}分{Seconds:0.000}秒", project.Name,
|
||||
elapsedTime.Hours * 60 + elapsedTime.Minutes, elapsedTime.TotalSeconds % 60);
|
||||
_logger.LogInformation("------------------------------");
|
||||
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
@@ -119,6 +156,7 @@ public partial class ScriptService : IScriptService
|
||||
{
|
||||
_logger.LogInformation("配置组 {Name} 执行结束", groupName);
|
||||
}
|
||||
Notify.Event(NotificationEvent.GroupEnd).Success($"配置组{groupName}结束");
|
||||
}
|
||||
|
||||
private List<ScriptGroupProject> ReloadScriptProjects(IEnumerable<ScriptGroupProject> projectList, ref bool hasTimer)
|
||||
@@ -175,6 +213,7 @@ public partial class ScriptService : IScriptService
|
||||
|
||||
private async Task ExecuteProject(ScriptGroupProject project)
|
||||
{
|
||||
|
||||
if (project.Type == "Javascript")
|
||||
{
|
||||
if (project.Project == null)
|
||||
@@ -218,7 +257,7 @@ public partial class ScriptService : IScriptService
|
||||
private static partial Regex DispatcherAddTimerRegex();
|
||||
|
||||
|
||||
public static async Task StartGameTask()
|
||||
public static async Task StartGameTask(bool waitForMainUi = true)
|
||||
{
|
||||
// 没启动时候,启动截图器
|
||||
var homePageViewModel = App.GetService<HomePageViewModel>();
|
||||
@@ -226,29 +265,33 @@ public partial class ScriptService : IScriptService
|
||||
{
|
||||
await homePageViewModel.OnStartTriggerAsync();
|
||||
|
||||
await Task.Run(() =>
|
||||
if (waitForMainUi)
|
||||
{
|
||||
var first = true;
|
||||
while (true)
|
||||
await Task.Run(() =>
|
||||
{
|
||||
if (!homePageViewModel.TaskDispatcherEnabled || !TaskContext.Instance().IsInitialized)
|
||||
var first = true;
|
||||
while (true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!homePageViewModel.TaskDispatcherEnabled || !TaskContext.Instance().IsInitialized)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var content = TaskControl.CaptureToRectArea();
|
||||
if (Bv.IsInMainUi(content) || Bv.IsInAnyClosableUi(content))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var content = TaskControl.CaptureToRectArea();
|
||||
if (Bv.IsInMainUi(content) || Bv.IsInAnyClosableUi(content))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
TaskControl.Logger.LogInformation("当前不在游戏主界面,等待进入主界面后执行任务...");
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
TaskControl.Logger.LogInformation("当前不在游戏主界面,等待进入主界面后执行任务...");
|
||||
TaskControl.Logger.LogInformation("如果你已经在游戏内的其他界面,请自行退出当前界面(ESC),使当前任务能够继续运行!");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,8 @@ public class UpdateService : IUpdateService
|
||||
private readonly ILogger<UpdateService> _logger;
|
||||
private readonly IConfigService _configService;
|
||||
|
||||
private const string HashUrl = "https://raw.githubusercontent.com/bettergi/bettergi-installation-data/refs/heads/main/hash.json";
|
||||
private const string NoticeUrl = "https://hui-config.oss-cn-hangzhou.aliyuncs.com/bgi/notice.json";
|
||||
private const string DownloadPageUrl = "https://bgi.huiyadan.com/download.html";
|
||||
private const string DownloadPageUrl = "https://bettergi.com/download.html";
|
||||
|
||||
public AllConfig Config { get; set; }
|
||||
|
||||
@@ -61,16 +60,22 @@ public class UpdateService : IUpdateService
|
||||
|
||||
if (!Global.IsNewVersion(newVersion))
|
||||
{
|
||||
if (option.Trigger == UpdateTrigger.Manual)
|
||||
{
|
||||
await MessageBox.InformationAsync("当前已是最新版本!");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Config.NotShowNewVersionNoticeEndVersion)
|
||||
&& !Global.IsNewVersion(Config.NotShowNewVersionNoticeEndVersion, newVersion))
|
||||
&& !Global.IsNewVersion(Config.NotShowNewVersionNoticeEndVersion, newVersion)
|
||||
&& option.Trigger == UpdateTrigger.Auto)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CheckUpdateWindow win = new()
|
||||
CheckUpdateWindow win = new(option)
|
||||
{
|
||||
Owner = Application.Current.MainWindow,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
@@ -78,7 +83,6 @@ public class UpdateService : IUpdateService
|
||||
UserInteraction = async (sender, button) =>
|
||||
{
|
||||
CheckUpdateWindow win = (CheckUpdateWindow)sender;
|
||||
CancellationTokenSource? tokenSource = new();
|
||||
|
||||
switch (button)
|
||||
{
|
||||
@@ -114,20 +118,8 @@ public class UpdateService : IUpdateService
|
||||
break;
|
||||
|
||||
case CheckUpdateWindow.CheckUpdateWindowButton.Cancel:
|
||||
if (tokenSource != null)
|
||||
{
|
||||
if (MessageBox.Question("正在更新中,确定要取消更新吗?") == MessageBoxResult.Yes)
|
||||
{
|
||||
win.ShowUpdateStatus = false;
|
||||
tokenSource?.Cancel();
|
||||
win.Close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
win.ShowUpdateStatus = false;
|
||||
win.Close();
|
||||
}
|
||||
win.ShowUpdateStatus = false;
|
||||
win.Close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,6 +324,7 @@
|
||||
"百晓",
|
||||
"伍德",
|
||||
"蓝川丞",
|
||||
"蓝砚",
|
||||
"松本",
|
||||
"甘乐",
|
||||
"卯师傅",
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.Web.WebView2.Wpf;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
@@ -31,6 +32,7 @@ public class WebpagePanel : UserControl
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureWebView2DataFolder();
|
||||
_webView = new WebView2()
|
||||
{
|
||||
CreationProperties = new CoreWebView2CreationProperties
|
||||
@@ -155,4 +157,19 @@ public class WebpagePanel : UserControl
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private void EnsureWebView2DataFolder()
|
||||
{
|
||||
try
|
||||
{
|
||||
string folder = Path.Combine(new FileInfo(Environment.ProcessPath!).DirectoryName!, @"WebView2Data\\");
|
||||
Directory.CreateDirectory(folder);
|
||||
DirectoryInfo info = new DirectoryInfo(folder);
|
||||
DirectorySecurity access = info.GetAccessControl();
|
||||
access.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, AccessControlType.Allow));
|
||||
info.SetAccessControl(access);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
<ui:SymbolIcon Symbol="AnimalTurtle24" />
|
||||
</ui:NavigationViewItem.Icon>
|
||||
</ui:NavigationViewItem>
|
||||
|
||||
|
||||
<ui:NavigationViewItem Content="全自动">
|
||||
<ui:NavigationViewItem.Icon>
|
||||
<ui:SymbolIcon Symbol="Bot24" />
|
||||
@@ -125,7 +125,7 @@
|
||||
</ui:NavigationViewItem>
|
||||
</ui:NavigationViewItem.MenuItems>
|
||||
</ui:NavigationViewItem>
|
||||
|
||||
|
||||
<ui:NavigationViewItem Content="辅助操控"
|
||||
NavigationCacheMode="Enabled"
|
||||
TargetPageType="{x:Type pages:MacroSettingsPage}">
|
||||
@@ -180,13 +180,14 @@
|
||||
<ui:TitleBar.Header>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
|
||||
<ui:Button Width="46"
|
||||
Height="32"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Command="{Binding SwitchBackdropCommand}"
|
||||
CornerRadius="0"
|
||||
Icon="{ui:SymbolIcon Blur24}"
|
||||
ToolTip="{Binding CurrentBackdropType}" />
|
||||
Height="32"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Command="{Binding SwitchBackdropCommand}"
|
||||
CornerRadius="0"
|
||||
Icon="{ui:SymbolIcon Blur24}"
|
||||
ToolTip="切换毛玻璃效果"
|
||||
Visibility="{Binding IsWin11Later, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}" />
|
||||
<ui:Button Width="46"
|
||||
Height="32"
|
||||
Background="Transparent"
|
||||
|
||||
@@ -19,6 +19,7 @@ using System.Windows.Documents;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using BetterGenshinImpact.Genshin.Settings2;
|
||||
using Vanara.PInvoke;
|
||||
using FontFamily = System.Windows.Media.FontFamily;
|
||||
|
||||
@@ -174,9 +175,22 @@ public partial class MaskWindow : Window
|
||||
{
|
||||
_logger.LogError("当前游戏分辨率不是16:9,一条龙、配队识别、地图传送、地图追踪等所有独立任务与全自动任务相关功能,都将会无法正常使用!");
|
||||
}
|
||||
|
||||
AfterburnerWarning();
|
||||
|
||||
// 读取游戏注册表配置
|
||||
// ReadGameSettings();
|
||||
GameSettingsChecker.LoadGameSettingsAndCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* MSIAfterburner.exe 在左上角会导致识别失败
|
||||
*/
|
||||
private void AfterburnerWarning()
|
||||
{
|
||||
if (Process.GetProcessesByName("MSIAfterburner").Length > 0)
|
||||
{
|
||||
_logger.LogWarning("检测到 MSI Afterburner 正在运行,如果信息位于特定UI上遮盖图像识别要素可能导致识别失败,请关闭MSI Afterburner 或者调整信息位置后重试!");
|
||||
}
|
||||
}
|
||||
|
||||
// private void ReadGameSettings()
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helpers="clr-namespace:BetterGenshinImpact.Helpers"
|
||||
xmlns:local="clr-namespace:BetterGenshinImpact.View.Pages"
|
||||
xmlns:markup="clr-namespace:BetterGenshinImpact.Markup"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:pages="clr-namespace:BetterGenshinImpact.ViewModel.Pages"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
@@ -81,7 +78,9 @@
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
TextWrapping="Wrap">
|
||||
在遮罩内显示日志窗口,<Hyperlink Command="{Binding GoToLogFolderCommand}" Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
|
||||
在遮罩内显示日志窗口,
|
||||
<Hyperlink Command="{Binding GoToLogFolderCommand}"
|
||||
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
|
||||
点击打开日志文件夹
|
||||
</Hyperlink>
|
||||
</ui:TextBlock>
|
||||
@@ -333,7 +332,9 @@
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
TextWrapping="Wrap">
|
||||
可以通过快捷键保存截图,文件保存在<Hyperlink Command="{Binding GoToFolderCommand}" Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
|
||||
可以通过快捷键保存截图,文件保存在
|
||||
<Hyperlink Command="{Binding GoToFolderCommand}"
|
||||
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
|
||||
log/screenshot
|
||||
</Hyperlink>
|
||||
</ui:TextBlock>
|
||||
@@ -431,11 +432,98 @@
|
||||
</ui:CardControl.Header>
|
||||
<ui:ToggleSwitch Margin="0,0,36,0" IsChecked="{Binding Config.CommonConfig.ExitToTray, Mode=TwoWay}" />
|
||||
</ui:CardControl>
|
||||
|
||||
|
||||
<ui:TextBlock Margin="0,0,0,8"
|
||||
FontTypography="BodyStrong"
|
||||
Text="通知设置" />
|
||||
|
||||
<!-- 全局通知设置 -->
|
||||
<ui:CardExpander Margin="0,0,0,12"
|
||||
ContentPadding="0"
|
||||
Icon="{ui:SymbolIcon Alert24}">
|
||||
<ui:CardExpander.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="全局通知设置"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="影响下方所有通知的设置"
|
||||
TextWrapping="Wrap" />
|
||||
<!--<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,24,0"
|
||||
IsChecked="{Binding Config.NotificationConfig.WebhookEnabled, Mode=TwoWay}" />-->
|
||||
</Grid>
|
||||
</ui:CardExpander.Header>
|
||||
<StackPanel>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="通知时包含截图"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="总是在通知中包含截图"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
IsChecked="{Binding Config.NotificationConfig.IncludeScreenShot, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="需要通知的事件"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="英文逗号分割,为空为全部通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="220"
|
||||
MaxWidth="800"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.NotificationConfig.NotificationEventSubscribe, Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<!-- Webhook -->
|
||||
<ui:CardExpander Margin="0,0,0,12"
|
||||
ContentPadding="0"
|
||||
@@ -453,7 +541,7 @@
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="启用Webhook"
|
||||
Text="启用 Webhook"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
@@ -533,6 +621,405 @@
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<!-- Windows 通知 -->
|
||||
<ui:CardExpander Margin="0,0,0,12"
|
||||
ContentPadding="0"
|
||||
Icon="{ui:SymbolIcon CommentArrowLeft24}">
|
||||
<ui:CardExpander.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="启用 Windows 通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="Windows 通知别与游戏界面重叠,否则易误点通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,24,0"
|
||||
IsChecked="{Binding Config.NotificationConfig.WindowsUwpNotificationEnabled, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</ui:CardExpander.Header>
|
||||
<StackPanel>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="测试 Windows 通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="发送测试通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:Button Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
Command="{Binding TestWindowsUwpNotificationCommand}"
|
||||
Content="发送" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<ui:CardExpander Margin="0,0,0,12"
|
||||
ContentPadding="0"
|
||||
Icon="{ui:SymbolIcon CommentArrowLeft24}">
|
||||
<ui:CardExpander.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="启用飞书通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="飞书通知相关设置"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,24,0"
|
||||
IsChecked="{Binding Config.NotificationConfig.FeishuNotificationEnabled, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</ui:CardExpander.Header>
|
||||
<StackPanel>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="3*" />
|
||||
<ColumnDefinition Width="2*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="飞书通知地址"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="填写飞书通知地址"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="180"
|
||||
MaxWidth="800"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.NotificationConfig.FeishuWebhookUrl, Mode=TwoWay}"
|
||||
TextWrapping="NoWrap" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="测试飞书通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="发送测试通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:Button Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
Command="{Binding TestFeishuNotificationCommand}"
|
||||
Content="发送" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<ui:CardExpander Margin="0,0,0,12"
|
||||
ContentPadding="0"
|
||||
Icon="{ui:SymbolIcon CommentArrowLeft24}">
|
||||
<ui:CardExpander.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="启用企业微信通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="企业微信通知相关设置"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,24,0"
|
||||
IsChecked="{Binding Config.NotificationConfig.WorkweixinNotificationEnabled, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</ui:CardExpander.Header>
|
||||
<StackPanel>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="3*" />
|
||||
<ColumnDefinition Width="2*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="企业微信通知地址"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="填写企业微信通知地址"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="180"
|
||||
MaxWidth="800"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.NotificationConfig.WorkweixinWebhookUrl, Mode=TwoWay}"
|
||||
TextWrapping="NoWrap" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="测试企业微信通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="发送测试通知"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:Button Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
Command="{Binding TestWorkWeixinNotificationCommand}"
|
||||
Content="发送" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<ui:TextBlock Margin="0,0,0,8"
|
||||
FontTypography="BodyStrong"
|
||||
Text="通用功能置" />
|
||||
<!-- 快速传送 -->
|
||||
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0">
|
||||
<ui:CardExpander.Icon>
|
||||
<ui:FontIcon Glyph="" Style="{StaticResource FaFontIconStyle}" />
|
||||
</ui:CardExpander.Icon>
|
||||
<ui:CardExpander.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="大地图地图传送设置"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="用于地图追踪、自动秘境、一条龙等功能中传送功能的配置"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</ui:CardExpander.Header>
|
||||
<StackPanel>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="地图移动过程中是否缩放地图"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="建议开启,关闭该设置可能在运行部分脚本时发生错误"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
IsChecked="{Binding Config.TpConfig.MapZoomEnabled, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="单次鼠标移动的最大距离"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="过大可能会导致鼠标移动出窗口"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.TpConfig.MaxMouseMove, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="地图缩小的距离"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="大于这个距离会缩小地图以加快传送"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.TpConfig.MapZoomOutDistance, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="地图放大的距离"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="小于这个距离会放大地图以提高移动的精度"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.TpConfig.MapZoomInDistance, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="鼠标移动的时间间隔(毫秒)"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="数字越小移动鼠标的速度越快,如果移动地图时产生卡顿,请提高这个数值"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.TpConfig.StepIntervalMilliseconds, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
<!-- 地图 -->
|
||||
<!--<ui:CardControl Margin="0,0,0,12"
|
||||
Icon="{ui:SymbolIcon Cursor24}"
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<Grid>
|
||||
<Border ClipToBounds="True" CornerRadius="8">
|
||||
<Border.Background>
|
||||
<ImageBrush ImageSource="pack://application:,,,/Assets/Images/banner_sayu.jpg"
|
||||
<ImageBrush ImageSource="pack://application:,,,/Assets/Images/banner.jpg"
|
||||
RenderOptions.BitmapScalingMode="HighQuality"
|
||||
Stretch="UniformToFill" />
|
||||
</Border.Background>
|
||||
|
||||
@@ -473,7 +473,7 @@
|
||||
|
||||
<ui:CardControl Margin="0,0,0,12">
|
||||
<ui:CardControl.Icon>
|
||||
<ui:FontIcon Glyph="" Style="{StaticResource FaFontIconStyle}" />
|
||||
<ui:FontIcon Glyph="" Style="{StaticResource FaFontIconStyle}" />
|
||||
</ui:CardControl.Icon>
|
||||
<ui:CardControl.Header>
|
||||
<Grid>
|
||||
@@ -541,7 +541,7 @@
|
||||
|
||||
<ui:CardControl Margin="0,0,0,12">
|
||||
<ui:CardControl.Icon>
|
||||
<ui:FontIcon Glyph="" Style="{StaticResource FaFontIconStyle}" />
|
||||
<ui:FontIcon Glyph="" Style="{StaticResource FaFontIconStyle}" />
|
||||
</ui:CardControl.Icon>
|
||||
<ui:CardControl.Header>
|
||||
<Grid>
|
||||
|
||||
@@ -217,6 +217,7 @@
|
||||
<MenuItem Header="日志分析" Command="{Binding OpenLogParseCommand}" />
|
||||
<MenuItem Header="打开脚本仓库" Command="{Binding OpenLocalScriptRepoCommand}" />
|
||||
<MenuItem Header="根据文件夹更新" Command="{Binding UpdateTasksCommand}" />
|
||||
<MenuItem Header="任务倒序排列" Command="{Binding ReverseTaskOrderCommand}" />
|
||||
</ContextMenu>
|
||||
</ui:DropDownButton.Flyout>
|
||||
</ui:DropDownButton>
|
||||
@@ -330,6 +331,22 @@
|
||||
<MenuItem Command="{Binding DeleteScriptCommand}"
|
||||
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"
|
||||
Header="移除" />
|
||||
<MenuItem Command="{Binding DeleteScriptByFolderCommand}"
|
||||
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"
|
||||
Header="根据文件夹移除">
|
||||
<MenuItem.Style>
|
||||
<Style TargetType="MenuItem">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger
|
||||
Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem.Type}"
|
||||
Value="Pathing">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</MenuItem.Style>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</ListView.ContextMenu>
|
||||
<ListView.Style>
|
||||
|
||||
@@ -1287,7 +1287,7 @@
|
||||
</ui:TextBlock>
|
||||
</Grid>
|
||||
</ui:CardControl.Header>
|
||||
<controls:TwoStateButton Margin="0,0,36,0"
|
||||
<controls:TwoStateButton Margin="0,0,40,0"
|
||||
DisableCommand="{Binding StopSoloTaskCommand}"
|
||||
DisableContent="停止"
|
||||
EnableCommand="{Binding SwitchAutoMusicGameCommand}"
|
||||
@@ -1296,7 +1296,7 @@
|
||||
</ui:CardControl>
|
||||
|
||||
<!-- 自动音游专辑 -->
|
||||
<ui:CardExpander Margin="0,0,0,12">
|
||||
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0">
|
||||
<ui:CardExpander.Icon>
|
||||
<ui:FontIcon Glyph="" Style="{StaticResource FaFontIconStyle}" />
|
||||
</ui:CardExpander.Icon>
|
||||
@@ -1350,12 +1350,37 @@
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="自定义难度等级,使该级别下所有乐曲达成【大音天籁】"
|
||||
Text="自动演奏未达成【大音天籁】的乐曲"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="默认为【传说】,【大师】即可获取所有奖励,【所有】即全部难度等级的所有乐曲达成【大音天籁】。"
|
||||
Text="关闭时,奖励已经领取就会跳过乐曲。开启时,达成了【大音天籁】才会跳过乐曲"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
IsChecked="{Binding Config.AutoMusicGameConfig.MustCanorusLevel, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="自动演奏的目标难度选择"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="设置为【传说】,【大师】即可获取所有奖励,设置【所有】则会对乐曲的所有难度进行自动演奏"
|
||||
TextWrapping="Wrap" />
|
||||
<ComboBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
|
||||
@@ -779,165 +779,7 @@
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<!-- 快速传送 -->
|
||||
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0">
|
||||
<ui:CardExpander.Icon>
|
||||
<ui:FontIcon Glyph="" Style="{StaticResource FaFontIconStyle}" />
|
||||
</ui:CardExpander.Icon>
|
||||
<ui:CardExpander.Header>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="地图移动"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="传送点不在当前地图中时,传送过程中移动地图"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</ui:CardExpander.Header>
|
||||
<StackPanel>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="地图移动过程中是否缩放地图"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="建议开启,关闭该设置可能在运行部分脚本时发生错误"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:ToggleSwitch Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,36,0"
|
||||
IsChecked="{Binding Config.TpConfig.MapZoomEnabled, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="单次鼠标移动的最大距离"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="过大可能会导致鼠标移动出窗口,单位:像素"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.TpConfig.MaxMouseMove, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="地图缩小的距离"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="大于这个距离会缩小地图以加快传送,单位:像素"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.TpConfig.MapZoomOutDistance, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="地图放大的距离"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="小于这个距离会放大地图以提高移动的精度,单位:像素"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.TpConfig.MapZoomInDistance, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="鼠标移动的时间间隔"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="数字越小移动鼠标的速度越快,如果移动地图时产生卡顿,请提高这个数值,单位:毫秒"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="90"
|
||||
Margin="0,0,36,0"
|
||||
Text="{Binding Config.TpConfig.StepIntervalMilliseconds, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
|
||||
<!-- 自动烹饪 -->
|
||||
<ui:CardControl Margin="0,0,0,12">
|
||||
<ui:CardControl.Icon>
|
||||
|
||||
@@ -282,6 +282,32 @@
|
||||
IsChecked="{Binding PathingConfig.SoloTaskUseFightEnabled, Mode=TwoWay}" />
|
||||
|
||||
</Grid>
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
FontTypography="Body"
|
||||
Text="不在某时执行"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBlock Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="当执行完一个路线后,如果时间为当前配置的时间(范围:0-23),则此路径追踪任务后续都将都跳过,适用于连续执行的兜底任务,例如想通宵挂机,并且在4点后,开始执行新的任务。"
|
||||
TextWrapping="Wrap" />
|
||||
<ui:TextBox Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
MinWidth="100"
|
||||
Text="{Binding PathingConfig.SkipDuring, Mode=TwoWay}"
|
||||
TextWrapping="NoWrap" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</ui:CardExpander>
|
||||
<ui:CardExpander Margin="0,0,0,12"
|
||||
|
||||
@@ -51,7 +51,8 @@
|
||||
Appearance="Success"
|
||||
Command="{Binding UpdateCommand}"
|
||||
Content="立即更新" />
|
||||
<ui:Button Grid.Column="2"
|
||||
<ui:Button Name="IgnoreButton"
|
||||
Grid.Column="2"
|
||||
MinWidth="90"
|
||||
Margin="8,0,8,0"
|
||||
Command="{Binding IgnoreCommand}"
|
||||
|
||||
@@ -3,6 +3,8 @@ using CommunityToolkit.Mvvm.Input;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using BetterGenshinImpact.Model;
|
||||
using Wpf.Ui.Controls;
|
||||
|
||||
namespace BetterGenshinImpact.View.Windows;
|
||||
@@ -18,11 +20,16 @@ public partial class CheckUpdateWindow : FluentWindow
|
||||
[ObservableProperty]
|
||||
private string updateStatusMessage = string.Empty;
|
||||
|
||||
public CheckUpdateWindow()
|
||||
public CheckUpdateWindow(UpdateOption option)
|
||||
{
|
||||
DataContext = this;
|
||||
InitializeComponent();
|
||||
|
||||
if (option.Trigger == UpdateTrigger.Manual)
|
||||
{
|
||||
IgnoreButton.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
Closing += OnClosing;
|
||||
}
|
||||
|
||||
@@ -92,4 +99,4 @@ public partial class CheckUpdateWindow : FluentWindow
|
||||
Ignore,
|
||||
Cancel,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,13 +34,16 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
|
||||
public string Title => $"BetterGI · 更好的原神 · {Global.Version}{(RuntimeHelper.IsDebug ? " · Dev" : string.Empty)}";
|
||||
|
||||
[ObservableProperty]
|
||||
public bool _isVisible = true;
|
||||
private bool _isVisible = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public WindowState _windowState = WindowState.Normal;
|
||||
private WindowState _windowState = WindowState.Normal;
|
||||
|
||||
[ObservableProperty]
|
||||
public WindowBackdropType _currentBackdropType = WindowBackdropType.Auto;
|
||||
private WindowBackdropType _currentBackdropType = WindowBackdropType.Auto;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isWin11Later = OsVersionHelper.IsWindows11_OrGreater;
|
||||
|
||||
public AllConfig Config { get; set; }
|
||||
|
||||
@@ -102,22 +105,30 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
|
||||
{
|
||||
// 预热OCR
|
||||
await OcrPreheating();
|
||||
|
||||
|
||||
if (Environment.GetCommandLineArgs().Length > 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 自动处理目录配置
|
||||
await Patch1();
|
||||
|
||||
|
||||
// 首次运行
|
||||
if (Config.CommonConfig.IsFirstRun)
|
||||
{
|
||||
// 自动初始化键位绑定
|
||||
InitKeyBinding();
|
||||
// InitKeyBinding();
|
||||
Config.AutoFightConfig.TeamNames = ""; // 此配置以后无用
|
||||
Config.CommonConfig.IsFirstRun = false;
|
||||
|
||||
}
|
||||
// 版本是否运行过
|
||||
if (Config.CommonConfig.RunForVersion != Global.Version)
|
||||
{
|
||||
ModifyFolderSecurity();
|
||||
Config.CommonConfig.RunForVersion = Global.Version;
|
||||
}
|
||||
|
||||
// 检查更新
|
||||
@@ -131,9 +142,23 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
|
||||
|
||||
// 更新仓库
|
||||
ScriptRepoUpdater.Instance.AutoUpdate();
|
||||
|
||||
// 清理临时目录
|
||||
TempManager.CleanUp();
|
||||
}
|
||||
|
||||
|
||||
private void ModifyFolderSecurity()
|
||||
{
|
||||
// 检查程序是否位于C盘
|
||||
if (Global.StartUpPath.StartsWith(@"C:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 修改文件夹权限
|
||||
SecurityControlHelper.AllowFullFolderSecurity(Global.StartUpPath);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private void InitKeyBinding()
|
||||
{
|
||||
try
|
||||
@@ -151,6 +176,7 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
|
||||
MessageBox.Error("读取原神键位并设置键位绑定数据时发生异常:" + e.Message + ",后续可以手动设置");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* 不同的安装目录处理
|
||||
@@ -158,19 +184,28 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
|
||||
*/
|
||||
private async Task Patch1()
|
||||
{
|
||||
if (Directory.Exists(Global.Absolute("BetterGI"))
|
||||
// && File.Exists(Global.Absolute("BetterGI/BetterGI.exe"))
|
||||
&& Directory.Exists(Global.Absolute("BetterGI/User"))
|
||||
var embeddedPath = Global.Absolute("BetterGI");
|
||||
var embeddedUserPath = Global.Absolute("BetterGI/User");
|
||||
var exePath = Global.Absolute("BetterGI/BetterGI.exe");
|
||||
if (Directory.Exists(embeddedPath)
|
||||
&& File.Exists(exePath)
|
||||
&& Directory.Exists(embeddedUserPath)
|
||||
)
|
||||
{
|
||||
var res = await MessageBox.ShowAsync("检测到旧的 BetterGI 配置,是否迁移配置并清理旧目录?", "BetterGI", System.Windows.MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (res == System.Windows.MessageBoxResult.Yes)
|
||||
var fileVersionInfo = FileVersionInfo.GetVersionInfo(exePath);
|
||||
// 低版本才需要迁移
|
||||
if (fileVersionInfo.FileVersion != null && !Global.IsNewVersion(fileVersionInfo.FileVersion))
|
||||
{
|
||||
var dir = Global.Absolute("BetterGI/User");
|
||||
// 迁移配置,拷贝整个目录并覆盖
|
||||
DirectoryHelper.CopyDirectory(dir, Global.Absolute("User"));
|
||||
// 删除旧目录
|
||||
DirectoryHelper.DeleteDirectoryRecursively(Global.Absolute("BetterGI"));
|
||||
var res = await MessageBox.ShowAsync("检测到旧的 BetterGI 配置,是否迁移配置并清理旧目录?", "BetterGI", System.Windows.MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (res == System.Windows.MessageBoxResult.Yes)
|
||||
{
|
||||
// 迁移配置,拷贝整个目录并覆盖
|
||||
DirectoryHelper.CopyDirectory(embeddedUserPath, Global.Absolute("User"));
|
||||
// 删除旧目录
|
||||
DirectoryHelper.DeleteReadOnlyDirectory(embeddedPath);
|
||||
await MessageBox.InformationAsync("迁移配置成功, 软件将自动退出,请手动重新启动 BetterGI!");
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,11 +224,11 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
_logger.LogError("PaddleOcr预热异常,解决方案:https://bgi.huiyadan.com/faq.html:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
|
||||
_logger.LogError("PaddleOcr预热异常,解决方案:https://bettergi.com/faq.html:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
|
||||
var innerException = e.InnerException;
|
||||
if (innerException != null)
|
||||
{
|
||||
_logger.LogError("PaddleOcr预热内部异常,解决方案:https://bgi.huiyadan.com/faq.html:" + innerException.Source + "\r\n--" + Environment.NewLine + innerException.StackTrace + "\r\n---" + Environment.NewLine + innerException.Message);
|
||||
_logger.LogError("PaddleOcr预热内部异常,解决方案:https://bettergi.com/faq.html:" + innerException.Source + "\r\n--" + Environment.NewLine + innerException.StackTrace + "\r\n---" + Environment.NewLine + innerException.Message);
|
||||
throw innerException;
|
||||
}
|
||||
else
|
||||
@@ -205,7 +240,7 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MessageBox.Warning("PaddleOcr预热失败,解决方案:https://bgi.huiyadan.com/faq.html," + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
|
||||
MessageBox.Warning("PaddleOcr预热失败,解决方案:https://bettergi.com/faq.html," + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using BetterGenshinImpact.Model;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace BetterGenshinImpact.ViewModel;
|
||||
@@ -44,40 +45,11 @@ public partial class NotifyIconViewModel : ObservableObject
|
||||
[RelayCommand]
|
||||
public async Task CheckUpdateAsync()
|
||||
{
|
||||
try
|
||||
// 检查更新
|
||||
await App.GetService<IUpdateService>()!.CheckUpdateAsync(new UpdateOption
|
||||
{
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
|
||||
string jsonString = await httpClient.GetStringAsync(@"https://api.github.com/repos/babalae/better-genshin-impact/releases/latest");
|
||||
var jsonDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonString);
|
||||
|
||||
if (jsonDict != null)
|
||||
{
|
||||
string? name = jsonDict["name"] as string;
|
||||
string? body = jsonDict["body"] as string;
|
||||
string md = $"# {name}{new string('\n', 2)}{body}";
|
||||
|
||||
md = WebUtility.HtmlEncode(md);
|
||||
string md2html = ResourceHelper.GetString($"pack://application:,,,/Assets/Strings/md2html.html", Encoding.UTF8);
|
||||
var html = md2html.Replace("{{content}}", md);
|
||||
|
||||
WebpageWindow win = new()
|
||||
{
|
||||
Title = "更新日志",
|
||||
Width = 800,
|
||||
Height = 600,
|
||||
Owner = Application.Current.MainWindow,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner
|
||||
};
|
||||
|
||||
win.NavigateToHtml(html);
|
||||
win.ShowDialog();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_ = e;
|
||||
}
|
||||
Trigger = UpdateTrigger.Manual
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +72,7 @@ file static class WindowBacktray
|
||||
{
|
||||
window.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
if (window.WindowState == WindowState.Minimized)
|
||||
{
|
||||
nint hWnd = new WindowInteropHelper(Application.Current.MainWindow).Handle;
|
||||
@@ -107,4 +80,4 @@ file static class WindowBacktray
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ using BetterGenshinImpact.Service.Notifier;
|
||||
using BetterGenshinImpact.View;
|
||||
using Wpf.Ui;
|
||||
using Wpf.Ui.Controls;
|
||||
using Wpf.Ui.Violeta.Controls;
|
||||
|
||||
namespace BetterGenshinImpact.ViewModel.Pages;
|
||||
|
||||
@@ -23,12 +24,14 @@ public partial class CommonSettingsPageViewModel : ObservableObject, INavigation
|
||||
public AllConfig Config { get; set; }
|
||||
|
||||
private readonly INavigationService _navigationService;
|
||||
|
||||
private readonly NotificationService _notificationService;
|
||||
|
||||
[ObservableProperty] private bool _isLoading;
|
||||
|
||||
[ObservableProperty] private string _webhookStatus = string.Empty;
|
||||
private readonly NotificationService _notificationService;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _webhookStatus = string.Empty;
|
||||
|
||||
|
||||
public CommonSettingsPageViewModel(IConfigService configService, INavigationService navigationService, NotificationService notificationService)
|
||||
@@ -51,7 +54,7 @@ public partial class CommonSettingsPageViewModel : ObservableObject, INavigation
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<object>(this, "RefreshSettings", new object(), "重新计算控件位置"));
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private void OnSwitchMaskEnabled()
|
||||
{
|
||||
@@ -106,7 +109,7 @@ public partial class CommonSettingsPageViewModel : ObservableObject, INavigation
|
||||
|
||||
Process.Start("explorer.exe", path);
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OnTestWebhook()
|
||||
{
|
||||
@@ -119,4 +122,46 @@ public partial class CommonSettingsPageViewModel : ObservableObject, INavigation
|
||||
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OnTestWindowsUwpNotification()
|
||||
{
|
||||
var res = await _notificationService.TestNotifierAsync<WindowsUwpNotifier>();
|
||||
if(res.IsSuccess)
|
||||
{
|
||||
Toast.Success(res.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.Error(res.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OnTestFeishuNotification()
|
||||
{
|
||||
var res = await _notificationService.TestNotifierAsync<FeishuNotifier>();
|
||||
if(res.IsSuccess)
|
||||
{
|
||||
Toast.Success(res.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.Error(res.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task OnTestWorkWeixinNotification()
|
||||
{
|
||||
var res = await _notificationService.TestNotifierAsync<WorkWeixinNotifier>();
|
||||
if(res.IsSuccess)
|
||||
{
|
||||
Toast.Success(res.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
Toast.Error(res.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,19 +100,22 @@ public partial class HomePageViewModel : ObservableObject, INavigationAware, IVi
|
||||
private void OnLoaded()
|
||||
{
|
||||
// OnTest();
|
||||
|
||||
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
|
||||
// url protocol
|
||||
// BetterGI.dll bettergi://start/
|
||||
if (args.Length > 1)
|
||||
{
|
||||
if (args[1].Equals("start"))
|
||||
{
|
||||
_ = OnStartTriggerAsync();
|
||||
}
|
||||
else if (args[1].Equals("startOneDragon"))
|
||||
if (args[1].Contains("startOneDragon"))
|
||||
{
|
||||
var odVm = App.GetService<OneDragonFlowViewModel>();
|
||||
odVm?.OneKeyExecuteCommand.Execute(null);
|
||||
}
|
||||
else if (args[1].Contains("start"))
|
||||
{
|
||||
_ = OnStartTriggerAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +278,7 @@ public partial class HomePageViewModel : ObservableObject, INavigationAware, IVi
|
||||
|
||||
TaskDispatcherEnabled = false;
|
||||
_mouseKeyMonitor.Unsubscribe();
|
||||
TaskContext.Instance().IsInitialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,7 +304,7 @@ public partial class HomePageViewModel : ObservableObject, INavigationAware, IVi
|
||||
[RelayCommand]
|
||||
public void OnGoToWikiUrl()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/doc.html") { UseShellExecute = true });
|
||||
Process.Start(new ProcessStartInfo("https://bettergi.com/doc.html") { UseShellExecute = true });
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
@@ -28,9 +28,13 @@ using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BetterGenshinImpact.Core.Recognition.OCR;
|
||||
using BetterGenshinImpact.Core.Recognition.OpenCv;
|
||||
using BetterGenshinImpact.GameTask.AutoFight.Assets;
|
||||
using BetterGenshinImpact.GameTask.Model.Area;
|
||||
using BetterGenshinImpact.GameTask.QuickTeleport.Assets;
|
||||
using BetterGenshinImpact.View;
|
||||
using OpenCvSharp;
|
||||
using Vanara.PInvoke;
|
||||
using HotKeySettingModel = BetterGenshinImpact.Model.HotKeySettingModel;
|
||||
|
||||
@@ -595,9 +599,20 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
|
||||
// TaskControl.Logger.LogInformation("大地图界面缩放按钮位置:{Position}", Bv.GetBigMapScale( TaskControl.CaptureToRectArea()));
|
||||
|
||||
|
||||
TaskControl.Logger.LogInformation($"尝试显示遮罩窗口");
|
||||
var maskWindow = MaskWindow.Instance();
|
||||
maskWindow.Invoke(() => { maskWindow.Hide(); });
|
||||
// TaskControl.Logger.LogInformation($"尝试显示遮罩窗口");
|
||||
// var maskWindow = MaskWindow.Instance();
|
||||
// maskWindow.Invoke(() => { maskWindow.Show(); });
|
||||
Task.Run(async () => {
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var imageRegion = TaskControl.CaptureToRectArea();
|
||||
var eRa = imageRegion.DeriveCrop(AutoFightAssets.Instance.ECooldownRect);
|
||||
var eRaWhite = OpenCvCommonHelper.InRangeHsv(eRa.SrcMat, new Scalar(0, 0, 235), new Scalar(0, 25, 255));
|
||||
var text = OcrFactory.Paddle.OcrWithoutDetector(eRaWhite);
|
||||
TaskControl.Logger.LogInformation("冷却时间 {Num}", StringUtils.TryParseDouble(text));
|
||||
await Task.Delay(10);
|
||||
}
|
||||
});
|
||||
}
|
||||
));
|
||||
debugDirectory.Children.Add(new HotKeySettingModel(
|
||||
|
||||
@@ -118,7 +118,7 @@ public partial class JsListViewModel : ObservableObject, INavigationAware, IView
|
||||
[RelayCommand]
|
||||
public void OnGoToJsScriptUrl()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/feats/autos/jsscript.html") { UseShellExecute = true });
|
||||
Process.Start(new ProcessStartInfo("https://bettergi.com/feats/autos/jsscript.html") { UseShellExecute = true });
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
@@ -229,7 +229,7 @@ public partial class KeyMouseRecordPageViewModel : ObservableObject, INavigation
|
||||
[RelayCommand]
|
||||
public void OnGoToKmScriptUrl()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/feats/autos/kmscript.html") { UseShellExecute = true });
|
||||
Process.Start(new ProcessStartInfo("https://bettergi.com/feats/autos/kmscript.html") { UseShellExecute = true });
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
@@ -49,6 +49,6 @@ public partial class MacroSettingsPageViewModel : ObservableObject, INavigationA
|
||||
[RelayCommand]
|
||||
public void OnGoToOneKeyMacroUrl()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/feats/macro/onem.html") { UseShellExecute = true });
|
||||
Process.Start(new ProcessStartInfo("https://bettergi.com/feats/macro/onem.html") { UseShellExecute = true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public partial class MapPathingViewModel : ObservableObject, INavigationAware, I
|
||||
[RelayCommand]
|
||||
public void OnGoToPathingUrl()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/feats/autos/pathing.html") { UseShellExecute = true });
|
||||
Process.Start(new ProcessStartInfo("https://bettergi.com/feats/autos/pathing.html") { UseShellExecute = true });
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
@@ -14,6 +14,8 @@ using BetterGenshinImpact.GameTask;
|
||||
using BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||||
using BetterGenshinImpact.GameTask.Model.Enum;
|
||||
using BetterGenshinImpact.Service;
|
||||
using BetterGenshinImpact.Service.Notification;
|
||||
using BetterGenshinImpact.Service.Notification.Model.Enum;
|
||||
using BetterGenshinImpact.View.Windows;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Newtonsoft.Json;
|
||||
@@ -207,6 +209,7 @@ public partial class OneDragonFlowViewModel : ObservableObject, INavigationAware
|
||||
await new TaskRunner(DispatcherTimerOperationEnum.UseSelfCaptureImage)
|
||||
.RunThreadAsync(async () =>
|
||||
{
|
||||
Notify.Event(NotificationEvent.DragonStart).Success("一条龙启动");
|
||||
foreach (var task in TaskList)
|
||||
{
|
||||
if (task is { IsEnabled: true, Action: not null })
|
||||
@@ -215,6 +218,7 @@ public partial class OneDragonFlowViewModel : ObservableObject, INavigationAware
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
Notify.Event(NotificationEvent.DragonEnd).Success("一条龙结束");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using LogParse;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SharpCompress;
|
||||
using Wpf.Ui;
|
||||
using Wpf.Ui.Controls;
|
||||
using Wpf.Ui.Violeta.Controls;
|
||||
@@ -106,25 +107,32 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
[RelayCommand]
|
||||
private void ClearTasks()
|
||||
{
|
||||
if (SelectedScriptGroup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SelectedScriptGroup.Projects.Clear();
|
||||
WriteScriptGroup(SelectedScriptGroup);
|
||||
}
|
||||
[RelayCommand]
|
||||
private async Task OpenLogParse()
|
||||
{
|
||||
|
||||
GameInfo gameInfo = null;
|
||||
if (SelectedScriptGroup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GameInfo? gameInfo = null;
|
||||
var config = LogParse.LogParse.LoadConfig();
|
||||
if (!string.IsNullOrEmpty(config.Cookie))
|
||||
{
|
||||
config.CookieDictionary.TryGetValue(config.Cookie, out gameInfo);
|
||||
}
|
||||
LogParseConfig.ScriptGroupLogParseConfig sgpc;
|
||||
if (!config.ScriptGroupLogDictionary.TryGetValue(_selectedScriptGroup.Name,out sgpc))
|
||||
LogParseConfig.ScriptGroupLogParseConfig? sgpc;
|
||||
if (!config.ScriptGroupLogDictionary.TryGetValue(SelectedScriptGroup.Name,out sgpc))
|
||||
{
|
||||
sgpc=new LogParseConfig.ScriptGroupLogParseConfig();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 创建 StackPanel
|
||||
var stackPanel = new StackPanel
|
||||
@@ -174,6 +182,16 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
dayRangeComboBox.SelectedValuePath = "Value"; // 绑定的值
|
||||
dayRangeComboBox.SelectedIndex = 0;
|
||||
stackPanel.Children.Add(dayRangeComboBox);
|
||||
|
||||
// 开关控件:ToggleButton 或 CheckBox
|
||||
CheckBox faultStatsSwitch = new CheckBox
|
||||
{
|
||||
Content = "异常情况统计",
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
stackPanel.Children.Add(faultStatsSwitch);
|
||||
|
||||
|
||||
|
||||
|
||||
// 开关控件:ToggleButton 或 CheckBox
|
||||
@@ -212,9 +230,39 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
|
||||
secondRow.Children.Add(questionButton);
|
||||
|
||||
StackPanel threeRow = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Margin = new Thickness(0, 0, 0, 10)
|
||||
};
|
||||
|
||||
// 创建一个 TextBlock
|
||||
TextBlock hoeingDelayBlock = new TextBlock
|
||||
{
|
||||
Text = "锄地延时(秒):",
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
FontSize = 16,
|
||||
Margin = new Thickness(0, 0, 10, 0)
|
||||
};
|
||||
|
||||
|
||||
TextBox hoeingDelayTextBox = new TextBox
|
||||
{
|
||||
Width = 100,
|
||||
FontSize = 16,
|
||||
VerticalContentAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
threeRow.Children.Add(hoeingDelayBlock);
|
||||
threeRow.Children.Add(hoeingDelayTextBox);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 将第二行添加到 StackPanel
|
||||
stackPanel.Children.Add(secondRow);
|
||||
|
||||
stackPanel.Children.Add(threeRow);
|
||||
//PrimaryButtonText
|
||||
var uiMessageBox = new Wpf.Ui.Controls.MessageBox
|
||||
{
|
||||
@@ -224,9 +272,9 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
PrimaryButtonText = "确定",
|
||||
Owner = Application.Current.MainWindow,
|
||||
};
|
||||
questionButton.Click += (sender, args) =>
|
||||
{
|
||||
|
||||
void OnQuestionButtonOnClick(object sender, RoutedEventArgs args)
|
||||
{
|
||||
WebpageWindow cookieWin = new()
|
||||
{
|
||||
Title = "日志分析",
|
||||
@@ -237,15 +285,17 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
};
|
||||
cookieWin.NavigateToHtml(TravelsDiaryDetailManager.generHtmlMessage());
|
||||
cookieWin.Show();
|
||||
}
|
||||
|
||||
};
|
||||
questionButton.Click += OnQuestionButtonOnClick;
|
||||
|
||||
//对象赋值
|
||||
rangeComboBox.SelectedValue = sgpc.RangeValue;
|
||||
dayRangeComboBox.SelectedValue = sgpc.DayRangeValue;
|
||||
cookieTextBox.Text = config.Cookie;
|
||||
hoeingStatsSwitch.IsChecked = sgpc.HoeingStatsSwitch;
|
||||
|
||||
faultStatsSwitch.IsChecked = sgpc.FaultStatsSwitch;
|
||||
hoeingDelayTextBox.Text = sgpc.HoeingDelay;
|
||||
|
||||
MessageBoxResult result = await uiMessageBox.ShowDialogAsync();
|
||||
|
||||
@@ -259,8 +309,11 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
sgpc.DayRangeValue=dayRangeValue;
|
||||
sgpc.RangeValue = rangeValue;
|
||||
sgpc.HoeingStatsSwitch = hoeingStatsSwitch.IsChecked ?? false;
|
||||
sgpc.FaultStatsSwitch = faultStatsSwitch.IsChecked ?? false;
|
||||
sgpc.HoeingDelay = hoeingDelayTextBox.Text;
|
||||
|
||||
config.Cookie = cookieValue;
|
||||
config.ScriptGroupLogDictionary[_selectedScriptGroup.Name]=sgpc;
|
||||
config.ScriptGroupLogDictionary[SelectedScriptGroup.Name]=sgpc;
|
||||
|
||||
LogParse.LogParse.WriteConfigFile(config);
|
||||
|
||||
@@ -294,7 +347,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
Toast.Warning("未填写cookie,此次将不启用锄地统计!");
|
||||
}
|
||||
//真正存储的gameinfo
|
||||
GameInfo realGameInfo = gameInfo;
|
||||
GameInfo? realGameInfo = gameInfo;
|
||||
//统计锄地开关打开,并且不为cookie不为空
|
||||
if ((hoeingStatsSwitch.IsChecked ?? false) && !string.IsNullOrEmpty(cookieValue))
|
||||
{
|
||||
@@ -305,7 +358,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
Toast.Success($"米游社数据获取成功,开始进行解析,请耐心等待!");
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (Exception)
|
||||
{
|
||||
if (realGameInfo!=null)
|
||||
{
|
||||
@@ -333,7 +386,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
var configGroupEntities = LogParse.LogParse.ParseFile(fs);
|
||||
if (rangeValue == "CurrentConfig") {
|
||||
//Toast.Success(_selectedScriptGroup.Name);
|
||||
configGroupEntities =configGroupEntities.Where(item => _selectedScriptGroup.Name == item.Name).ToList();
|
||||
configGroupEntities =configGroupEntities.Where(item => SelectedScriptGroup.Name == item.Name).ToList();
|
||||
}
|
||||
if (configGroupEntities.Count == 0)
|
||||
{
|
||||
@@ -343,7 +396,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
configGroupEntities.Reverse();
|
||||
//realGameInfo
|
||||
//小怪摩拉统计
|
||||
win.NavigateToHtml(LogParse.LogParse.GenerHtmlByConfigGroupEntity(configGroupEntities,hoeingStats ? realGameInfo : null));
|
||||
win.NavigateToHtml(LogParse.LogParse.GenerHtmlByConfigGroupEntity(configGroupEntities,hoeingStats ? realGameInfo : null,sgpc));
|
||||
win.ShowDialog();
|
||||
}
|
||||
|
||||
@@ -369,11 +422,11 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
ScriptRepoUpdater.Instance.OpenLocalRepoInWebView();
|
||||
}
|
||||
[RelayCommand]
|
||||
private async Task UpdateTasks()
|
||||
private void UpdateTasks()
|
||||
{
|
||||
List<ScriptGroupProject> projects = new();
|
||||
List<ScriptGroupProject> oldProjects = new();
|
||||
oldProjects.AddRange(SelectedScriptGroup?.Projects);
|
||||
oldProjects.AddRange(SelectedScriptGroup?.Projects ?? []);
|
||||
var oldcount = oldProjects.Count;
|
||||
List<string> folderNames = new();
|
||||
foreach (var project in oldProjects)
|
||||
@@ -406,17 +459,26 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
}
|
||||
}
|
||||
|
||||
SelectedScriptGroup.Projects.Clear();
|
||||
SelectedScriptGroup?.Projects.Clear();
|
||||
foreach (var scriptGroupProject in projects)
|
||||
{
|
||||
SelectedScriptGroup?.AddProject(scriptGroupProject);
|
||||
}
|
||||
|
||||
Toast.Success($"增加了{projects.Count - oldcount}个路径追踪任务");
|
||||
WriteScriptGroup(SelectedScriptGroup);
|
||||
|
||||
if (SelectedScriptGroup != null) WriteScriptGroup(SelectedScriptGroup);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ReverseTaskOrder()
|
||||
{
|
||||
|
||||
List<ScriptGroupProject> projects = new();
|
||||
projects.AddRange(SelectedScriptGroup?.Projects.Reverse() ?? []);
|
||||
SelectedScriptGroup?.Projects.Clear();
|
||||
projects.ForEach(item=>SelectedScriptGroup?.Projects.Add(item));
|
||||
if (SelectedScriptGroup != null) WriteScriptGroup(SelectedScriptGroup);
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
public void OnCopyScriptGroup(ScriptGroup? item)
|
||||
@@ -444,8 +506,12 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
else
|
||||
{
|
||||
var newScriptGroup =JsonSerializer.Deserialize<ScriptGroup>(JsonSerializer.Serialize(item)) ;
|
||||
newScriptGroup.Name = str;
|
||||
ScriptGroups.Add(newScriptGroup);
|
||||
if (newScriptGroup != null)
|
||||
{
|
||||
newScriptGroup.Name = str;
|
||||
ScriptGroups.Add(newScriptGroup);
|
||||
}
|
||||
|
||||
//WriteScriptGroup(newScriptGroup);
|
||||
}
|
||||
}
|
||||
@@ -574,12 +640,26 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
private ScrollViewer CreatePathingScriptSelectionPanel(IEnumerable<FileTreeNode<PathingTask>> list)
|
||||
{
|
||||
var stackPanel = new StackPanel();
|
||||
CheckBox excludeCheckBox = new CheckBox
|
||||
{
|
||||
Content = "排除已选择过的目录",
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
};
|
||||
stackPanel.Children.Add(excludeCheckBox);
|
||||
|
||||
var filterTextBox = new TextBox
|
||||
{
|
||||
Margin = new Thickness(0, 0, 0, 10),
|
||||
PlaceholderText = "输入筛选条件..."
|
||||
PlaceholderText = "输入筛选条件...",
|
||||
};
|
||||
filterTextBox.TextChanged += delegate
|
||||
{
|
||||
ApplyFilter(stackPanel, list, filterTextBox.Text, excludeCheckBox.IsChecked);
|
||||
};
|
||||
excludeCheckBox.Click += delegate
|
||||
{
|
||||
ApplyFilter(stackPanel, list, filterTextBox.Text, excludeCheckBox.IsChecked);
|
||||
};
|
||||
filterTextBox.TextChanged += (s, e) => ApplyFilter(stackPanel, list, filterTextBox.Text);
|
||||
stackPanel.Children.Add(filterTextBox);
|
||||
AddNodesToPanel(stackPanel, list, 0, filterTextBox.Text);
|
||||
|
||||
@@ -593,14 +673,50 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
return scrollViewer;
|
||||
}
|
||||
|
||||
private void ApplyFilter(StackPanel parentPanel, IEnumerable<FileTreeNode<PathingTask>> nodes, string filter)
|
||||
private void ApplyFilter(StackPanel parentPanel, IEnumerable<FileTreeNode<PathingTask>> nodes, string filter,bool? excludeSelectedFolder = false)
|
||||
{
|
||||
if (parentPanel.Children.Count > 0 && parentPanel.Children[0] is TextBox filterTextBox)
|
||||
if (parentPanel.Children.Count > 0)
|
||||
{
|
||||
List<UIElement> removeElements = new List<UIElement>();
|
||||
foreach (UIElement parentPanelChild in parentPanel.Children)
|
||||
{
|
||||
if (parentPanelChild is FrameworkElement frameworkElement && frameworkElement.Name.StartsWith("dynamic_"))
|
||||
{
|
||||
removeElements.Add(frameworkElement);
|
||||
}
|
||||
|
||||
}
|
||||
removeElements.ForEach(parentPanel.Children.Remove);
|
||||
}
|
||||
|
||||
if (excludeSelectedFolder ?? false)
|
||||
{
|
||||
|
||||
List<string> skipFolderNames = SelectedScriptGroup?.Projects.ToList().Select(item=>item.FolderName).Distinct().ToList() ?? [];
|
||||
//复制Nodes
|
||||
string jsonString = JsonSerializer.Serialize(nodes);
|
||||
var copiedNodes = JsonSerializer.Deserialize<ObservableCollection<FileTreeNode<PathingTask>>>(jsonString);
|
||||
if (copiedNodes!=null)
|
||||
{
|
||||
//路径过滤
|
||||
copiedNodes = FileTreeNodeHelper.FilterTree(copiedNodes, skipFolderNames);
|
||||
copiedNodes = FileTreeNodeHelper.FilterEmptyNodes(copiedNodes);
|
||||
AddNodesToPanel(parentPanel, copiedNodes, 0,filter);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
AddNodesToPanel(parentPanel, nodes, 0,filter);
|
||||
}
|
||||
|
||||
|
||||
/*if (parentPanel.Children.Count > 0 && parentPanel.Children[1] is TextBox filterTextBox)
|
||||
{
|
||||
parentPanel.Children.Clear();
|
||||
parentPanel.Children.Add(filterTextBox); // 保留筛选框
|
||||
AddNodesToPanel(parentPanel, nodes, 0, filter);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private void AddNodesToPanel(StackPanel parentPanel, IEnumerable<FileTreeNode<PathingTask>> nodes, int depth, string filter)
|
||||
@@ -617,6 +733,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
Content = node.FileName,
|
||||
Tag = node.FilePath,
|
||||
Margin = new Thickness(depth * 30, 0, 0, 0) // 根据深度计算Margin
|
||||
,Name = "dynamic_"+Guid.NewGuid().ToString().Replace("-","_")
|
||||
};
|
||||
|
||||
if (node.IsDirectory)
|
||||
@@ -629,6 +746,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
Header = checkBox,
|
||||
Content = childPanel,
|
||||
IsExpanded = false // 默认不展开
|
||||
,Name = "dynamic_"+Guid.NewGuid().ToString().Replace("-","_")
|
||||
};
|
||||
|
||||
checkBox.Checked += (s, e) => SetChildCheckBoxesState(childPanel, true);
|
||||
@@ -739,7 +857,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
[RelayCommand]
|
||||
private void AddNextFlag(ScriptGroupProject? item)
|
||||
{
|
||||
if (item == null)
|
||||
if (item == null || SelectedScriptGroup == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -751,8 +869,8 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
nextScheduledTask.Remove(nst);
|
||||
}
|
||||
|
||||
nextScheduledTask.Add((SelectedScriptGroup?.Name, item.Index, item.FolderName, item.Name));
|
||||
foreach (var item1 in SelectedScriptGroup.Projects)
|
||||
nextScheduledTask.Add((SelectedScriptGroup?.Name ?? "", item.Index, item.FolderName, item.Name));
|
||||
foreach (var item1 in SelectedScriptGroup?.Projects ?? [])
|
||||
{
|
||||
item1.NextFlag = false;
|
||||
}
|
||||
@@ -824,7 +942,15 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
Toast.Warning("只有JS脚本才有自定义配置");
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void OnDeleteScriptByFolder(ScriptGroupProject? item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SelectedScriptGroup?.Projects.ToList().Where(item2=>item2.FolderName == item.FolderName).ForEach(OnDeleteScript);
|
||||
}
|
||||
[RelayCommand]
|
||||
public void OnDeleteScript(ScriptGroupProject? item)
|
||||
{
|
||||
@@ -1009,7 +1135,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
[RelayCommand]
|
||||
public void OnGoToScriptGroupUrl()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("https://bgi.huiyadan.com/feats/autos/dispatcher.html") { UseShellExecute = true });
|
||||
Process.Start(new ProcessStartInfo("https://bettergi.com/feats/autos/dispatcher.html") { UseShellExecute = true });
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -1198,6 +1324,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware
|
||||
},
|
||||
CloseButtonText = "关闭",
|
||||
PrimaryButtonText = "确认执行",
|
||||
Owner = Application.Current.MainWindow,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ using System.Threading.Tasks;
|
||||
using Windows.System;
|
||||
using BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||||
using BetterGenshinImpact.GameTask.Model.Enum;
|
||||
using BetterGenshinImpact.Helpers;
|
||||
using Wpf.Ui;
|
||||
using Wpf.Ui.Controls;
|
||||
using Wpf.Ui.Violeta.Controls;
|
||||
@@ -98,10 +99,10 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
|
||||
[ObservableProperty]
|
||||
private List<string> _domainNameList;
|
||||
|
||||
|
||||
public static List<string> ArtifactSalvageStarList = ["4", "3", "2", "1"];
|
||||
|
||||
[ObservableProperty]
|
||||
[ObservableProperty]
|
||||
private List<string> _autoMusicLevelList = ["传说", "大师", "困难", "普通", "所有"];
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -191,7 +192,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
[RelayCommand]
|
||||
public async Task OnGoToAutoGeniusInvokationUrlAsync()
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bgi.huiyadan.com/feats/task/tcg.html"));
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bettergi.com/feats/task/tcg.html"));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -206,7 +207,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
[RelayCommand]
|
||||
public async Task OnGoToAutoWoodUrlAsync()
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bgi.huiyadan.com/feats/task/felling.html"));
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bettergi.com/feats/task/felling.html"));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -228,7 +229,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
[RelayCommand]
|
||||
public async Task OnGoToAutoFightUrlAsync()
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bgi.huiyadan.com/feats/task/domain.html"));
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bettergi.com/feats/task/domain.html"));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -249,7 +250,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
{
|
||||
if (string.IsNullOrEmpty(Config.AutoFightConfig.StrategyName))
|
||||
{
|
||||
Toast.Warning("请先在【独立任务——自动战斗】下拉列表配置中选择战斗策略!");
|
||||
UIDispatcherHelper.Invoke(() => { Toast.Warning("请先在【独立任务——自动战斗】下拉列表配置中选择战斗策略!"); });
|
||||
path = string.Empty;
|
||||
return true;
|
||||
}
|
||||
@@ -262,7 +263,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
|
||||
if (!File.Exists(path) && !Directory.Exists(path))
|
||||
{
|
||||
Toast.Error("当前选择的自动战斗策略文件不存在");
|
||||
UIDispatcherHelper.Invoke(() => { Toast.Error("当前选择的自动战斗策略文件不存在"); });
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -272,7 +273,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
[RelayCommand]
|
||||
public async Task OnGoToAutoDomainUrlAsync()
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bgi.huiyadan.com/feats/task/domain.html"));
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bettergi.com/feats/task/domain.html"));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -313,7 +314,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
[RelayCommand]
|
||||
public async Task OnGoToAutoTrackUrlAsync()
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bgi.huiyadan.com/feats/task/track.html"));
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bettergi.com/feats/task/track.html"));
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
@@ -348,7 +349,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
[RelayCommand]
|
||||
public async Task OnGoToAutoTrackPathUrlAsync()
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bgi.huiyadan.com/feats/task/track.html"));
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bettergi.com/feats/task/track.html"));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -363,7 +364,7 @@ public partial class TaskSettingsPageViewModel : ObservableObject, INavigationAw
|
||||
[RelayCommand]
|
||||
public async Task OnGoToAutoMusicGameUrlAsync()
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bgi.huiyadan.com/feats/task/music.html"));
|
||||
await Launcher.LaunchUriAsync(new Uri("https://bettergi.com/feats/task/music.html"));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
|
||||
@@ -34,16 +34,5 @@ copy /y publish.7z .\MicaSetup\Resources\Setups\publish.7z
|
||||
if exist "%zipFile%" ( del /f /q "%zipfile%" )
|
||||
rename publish.7z %archiveFile%
|
||||
|
||||
@echo [build uninst using vs2022]
|
||||
msbuild MicaSetup\MicaSetup.Uninst.csproj /t:Rebuild /p:Configuration=Release /p:DeployOnBuild=true /p:PublishProfile=FolderProfile /restore
|
||||
|
||||
@echo [build setup using vs2022]
|
||||
copy /y .\MicaSetup\bin\Release\net472\MicaSetup.exe .\MicaSetup\Resources\Setups\Uninst.exe
|
||||
msbuild MicaSetup\MicaSetup.csproj /t:Build /p:Configuration=Release /p:DeployOnBuild=true /p:PublishProfile=FolderProfile /restore
|
||||
|
||||
@echo [finish]
|
||||
del /f /q MicaSetup.exe
|
||||
copy /y .\MicaSetup\bin\Release\net472\MicaSetup.exe .\
|
||||
rename MicaSetup.exe %setupFile%
|
||||
rd /s /q dist\BetterGI
|
||||
|
||||
|
||||
52
README.md
52
README.md
@@ -1,8 +1,8 @@
|
||||
<div align="center">
|
||||
<h1 align="center">
|
||||
<a href="https://bgi.huiyadan.com/"><img src="https://img.alicdn.com/imgextra/i2/2042484851/O1CN014wn1rf1lhoFYjL0gA_!!2042484851.png" width="200"></a>
|
||||
<a href="https://bettergi.com/"><img src="https://img.alicdn.com/imgextra/i2/2042484851/O1CN014wn1rf1lhoFYjL0gA_!!2042484851.png" width="200"></a>
|
||||
<br/>
|
||||
<a href="https://bgi.huiyadan.com/">BetterGI</a>
|
||||
<a href="https://bettergi.com/">BetterGI</a>
|
||||
</h1>
|
||||
<a href="https://trendshift.io/repositories/5269" target="_blank"><img src="https://trendshift.io/api/badge/repositories/5269" alt="babalae%2Fbetter-genshin-impact | Trendshift" style="width: 200px; height: 46px;" width="250" height="46"/></a>
|
||||
</div>
|
||||
@@ -20,21 +20,28 @@
|
||||
BetterGI · 更好的原神, 一个基于计算机视觉技术,意图让原神变的更好的项目。
|
||||
|
||||
## 功能
|
||||
* [自动拾取](https://bgi.huiyadan.com/doc.html#%E8%87%AA%E5%8A%A8%E6%8B%BE%E5%8F%96):遇到可交互/拾取内容时自动按 <kbd>F</kbd>,支持黑白名单配置
|
||||
* [自动剧情](https://bgi.huiyadan.com/doc.html#%E8%87%AA%E5%8A%A8%E5%89%A7%E6%83%85):快速点击过剧情、自动选择选项、自动提交物品、关闭弹出书页等
|
||||
* 与凯瑟琳对话时有橙色选项会 [自动领取「每日委托」奖励](https://bgi.huiyadan.com/doc.html#%E8%87%AA%E5%8A%A8%E9%A2%86%E5%8F%96%E3%80%8E%E6%AF%8F%E6%97%A5%E5%A7%94%E6%89%98%E3%80%8F%E5%A5%96%E5%8A%B1)、[自动重新派遣](https://bgi.huiyadan.com/doc.html#%E8%87%AA%E5%8A%A8%E9%87%8D%E6%96%B0%E6%B4%BE%E9%81%A3)
|
||||
* [自动邀约](https://bgi.huiyadan.com/doc.html#%E8%87%AA%E5%8A%A8%E9%82%80%E7%BA%A6):自动剧情开启的情况下此功能才会生效,自动选择邀约选项
|
||||
* [全自动钓鱼](https://bgi.huiyadan.com/doc.html#%E5%85%A8%E8%87%AA%E5%8A%A8%E9%92%93%E9%B1%BC):AI 识别自动抛竿,鱼上钩时自动收杆,并自动完成钓鱼进度
|
||||
* [自动烹饪](https://bgi.huiyadan.com/doc.html#%E8%87%AA%E5%8A%A8%E7%83%B9%E9%A5%AA):自动在完美区域完成食物烹饪,暂不支持“仙跳墙”
|
||||
* [全自动七圣召唤](https://bgi.huiyadan.com/doc.html#%E8%87%AA%E5%8A%A8%E4%B8%83%E5%9C%A3%E5%8F%AC%E5%94%A4):帮助你轻松完成七圣召唤角色邀请、每周来客挑战等 PVE 内容
|
||||
* [自动伐木](http://bgi.huiyadan.com/doc.html#%E8%87%AA%E5%8A%A8%E4%BC%90%E6%9C%A8):自动 <kbd>Z</kbd> 键使用「王树瑞佑」,利用上下线可以刷新木材的原理,挂机刷满一背包的木材
|
||||
* [自动战斗](http://bgi.huiyadan.com/feats/domain.html):编写战斗脚本,让队伍按照你的策略进行自动战斗
|
||||
* [自动秘境](http://bgi.huiyadan.com/feats/domain.html):全自动秘境挂机刷体力,自动循环进入秘境开启钥匙、战斗、走到古树并领取奖励
|
||||
* [快速传送](http://bgi.huiyadan.com/feats/domain.html):在地图上点击传送点,或者点击后出现的列表中存在传送点,会自动点击传送点并传送
|
||||
* [那维莱特转圈](https://bgi.huiyadan.com/doc.html#%E9%82%A3%E7%BB%B4%E8%8E%B1%E7%89%B9-%E8%BD%AC%E5%9C%88%E5%9C%88):设置快捷键后,长按可以不断水平旋转视角(当然你也可以用来转草神)
|
||||
* [快速圣遗物强化](https://bgi.huiyadan.com/doc.html#%E5%9C%A3%E9%81%97%E7%89%A9%E4%B8%80%E9%94%AE%E5%BC%BA%E5%8C%96):通过快速切换“详情”、“强化”页跳过圣遗物强化结果展示,快速+20
|
||||
* [商店一键购买](https://bgi.huiyadan.com/doc.html#%E5%9C%A3%E9%81%97%E7%89%A9%E4%B8%80%E9%94%AE%E5%BC%BA%E5%8C%96):可以快速以满数量购买商店中的物品,适合快速清空活动兑换,尘歌壶商店兑换等
|
||||
* [**……**](https://bgi.huiyadan.com/feat.html)
|
||||
* 实时任务
|
||||
* [自动拾取](https://bettergi.com/doc.html#%E8%87%AA%E5%8A%A8%E6%8B%BE%E5%8F%96):遇到可交互/拾取内容时自动按 <kbd>F</kbd>,支持黑白名单配置
|
||||
* [自动剧情](https://bettergi.com/doc.html#%E8%87%AA%E5%8A%A8%E5%89%A7%E6%83%85):快速点击过剧情、自动选择选项、自动提交物品、关闭弹出书页等
|
||||
* 与凯瑟琳对话时有橙色选项会 [自动领取「每日委托」奖励](https://bettergi.com/doc.html#%E8%87%AA%E5%8A%A8%E9%A2%86%E5%8F%96%E3%80%8E%E6%AF%8F%E6%97%A5%E5%A7%94%E6%89%98%E3%80%8F%E5%A5%96%E5%8A%B1)、[自动重新派遣](https://bettergi.com/doc.html#%E8%87%AA%E5%8A%A8%E9%87%8D%E6%96%B0%E6%B4%BE%E9%81%A3)
|
||||
* [自动邀约](https://bettergi.com/doc.html#%E8%87%AA%E5%8A%A8%E9%82%80%E7%BA%A6):自动剧情开启的情况下此功能才会生效,自动选择邀约选项
|
||||
* [快速传送](http://bettergi.com/feats/domain.html):在地图上点击传送点,或者点击后出现的列表中存在传送点,会自动点击传送点并传送
|
||||
* [全自动钓鱼](https://bettergi.com/doc.html#%E5%85%A8%E8%87%AA%E5%8A%A8%E9%92%93%E9%B1%BC):AI 识别自动抛竿,鱼上钩时自动收杆,并自动完成钓鱼进度
|
||||
* [自动烹饪](https://bettergi.com/doc.html#%E8%87%AA%E5%8A%A8%E7%83%B9%E9%A5%AA):自动在完美区域完成食物烹饪,暂不支持“仙跳墙”
|
||||
* 独立任务
|
||||
* [全自动七圣召唤](https://bettergi.com/doc.html#%E8%87%AA%E5%8A%A8%E4%B8%83%E5%9C%A3%E5%8F%AC%E5%94%A4):帮助你轻松完成七圣召唤角色邀请、每周来客挑战等 PVE 内容
|
||||
* [自动伐木](http://bettergi.com/doc.html#%E8%87%AA%E5%8A%A8%E4%BC%90%E6%9C%A8):自动 <kbd>Z</kbd> 键使用「王树瑞佑」,利用上下线可以刷新木材的原理,挂机刷满一背包的木材
|
||||
* [自动秘境](http://bettergi.com/feats/domain.html):全自动秘境挂机刷体力,自动循环进入秘境开启钥匙、战斗、走到古树并领取奖励
|
||||
* [自动音游](https://bettergi.com/feats/task/music.html):一键自动完成千音雅集的专辑,快速获取成就
|
||||
* 全自动
|
||||
* [一条龙](https://github.com/babalae/better-genshin-impact/issues/846):一键完成日常(使用历练点),并领取奖励
|
||||
* [自动采集/挖矿/锄地](https://bettergi.com/feats/autos/pathing.html):通过左上角小地图的识别,完成自动采集、挖矿、锄地等功能
|
||||
* [键鼠录制](https://bettergi.com/feats/autos/kmscript.html):可以录制回放当前的键鼠操作,建议配合调度器使用
|
||||
* 操控辅助
|
||||
* [那维莱特转圈](https://bettergi.com/doc.html#%E9%82%A3%E7%BB%B4%E8%8E%B1%E7%89%B9-%E8%BD%AC%E5%9C%88%E5%9C%88):设置快捷键后,长按可以不断水平旋转视角(当然你也可以用来转草神)
|
||||
* [快速圣遗物强化](https://bettergi.com/doc.html#%E5%9C%A3%E9%81%97%E7%89%A9%E4%B8%80%E9%94%AE%E5%BC%BA%E5%8C%96):通过快速切换“详情”、“强化”页跳过圣遗物强化结果展示,快速+20
|
||||
* [商店一键购买](https://bettergi.com/doc.html#%E5%9C%A3%E9%81%97%E7%89%A9%E4%B8%80%E9%94%AE%E5%BC%BA%E5%8C%96):可以快速以满数量购买商店中的物品,适合快速清空活动兑换,尘歌壶商店兑换等
|
||||
* [**……**](https://bettergi.com/doc.html)
|
||||
|
||||
<div align="center">
|
||||
<img src="https://github.com/babalae/better-genshin-impact/assets/15783049/57ab7c3c-709a-4cf3-8f64-1c78764c364c"/>
|
||||
@@ -51,9 +58,9 @@ BetterGI · 更好的原神, 一个基于计算机视觉技术,意图让原
|
||||
> [!NOTE]
|
||||
> 下载地址:[⚡Github 下载](https://github.com/babalae/better-genshin-impact/releases)
|
||||
>
|
||||
> 不知道下载哪个?第一次使用?请看:[快速上手](https://bgi.huiyadan.com/quickstart.html) , 遇到问题请先看:[常见问题](https://bgi.huiyadan.com/faq.html)
|
||||
> 不知道下载哪个?第一次使用?请看:[快速上手](https://bettergi.com/quickstart.html) , 遇到问题请先看:[常见问题](https://bettergi.com/faq.html)
|
||||
|
||||
最新编译版本(无地图特征数据)可以从自动构建中获取: [](https://ci.appveyor.com/project/huiyadanli/better-genshin-impact/build/artifacts)
|
||||
最新编译版本可以从自动构建中获取: [](https://github.com/babalae/bettergi-publish/actions)
|
||||
|
||||
## 使用方法
|
||||
由于图像识别比较吃性能,低配置电脑可能无法正常使用部分功能。
|
||||
@@ -72,16 +79,16 @@ BetterGI · 更好的原神, 一个基于计算机视觉技术,意图让原
|
||||
|
||||
**打开软件以后,在“启动”页选择好截图方式,点击启动按钮就可以享受 BetterGI 带来的便利了!**
|
||||
|
||||
详细使用指南请看:[快速上手](https://bgi.huiyadan.com/quickstart.html)
|
||||
详细使用指南请看:[快速上手](https://bettergi.com/quickstart.html)
|
||||
|
||||
具体功能效果与使用方式见:[文档](https://bgi.huiyadan.com/doc.html)
|
||||
具体功能效果与使用方式见:[文档](https://bettergi.com/doc.html)
|
||||
|
||||
## FAQ
|
||||
* 为什么需要管理员权限?
|
||||
* 因为游戏是以管理员权限启动的,软件不以管理员权限启动的话没有权限模拟鼠标点击。
|
||||
* 会不会封号?
|
||||
* 理论上不会被封。 **BetterGI 不会做出任何修改游戏文件、读写游戏内存等任何危害游戏本体的行为,单纯依靠视觉算法和模拟操作实现。** 但是mhy是自由的,用户条款上明确说明第三方软件/模拟操作是封号理由之一。当前方案还是存在被检测的可能。只能说请低调使用,请不要跳脸官方。
|
||||
* [更多常见问题...](https://bgi.huiyadan.com/faq.html)
|
||||
* [更多常见问题...](https://bettergi.com/faq.html)
|
||||
|
||||
## 致谢
|
||||
|
||||
@@ -94,6 +101,7 @@ BetterGI · 更好的原神, 一个基于计算机视觉技术,意图让原
|
||||
* [genshin_impact_assistant](https://github.com/infstellar/genshin_impact_assistant)
|
||||
* [HutaoFisher](https://github.com/myHuTao-qwq/HutaoFisher)
|
||||
* [minimap](https://github.com/tignioj/minimap)
|
||||
* [kachina-installer](https://github.com/YuehaiTeam/kachina-installer)
|
||||
|
||||
另外特别感谢 [@Lightczx](https://github.com/Lightczx) 和 [@emako](https://github.com/emako) 对本项目的指导与贡献
|
||||
|
||||
|
||||
Reference in New Issue
Block a user