Files
better-genshin-impact/BetterGenshinImpact/GameTask/AutoFight/AutoFightTask.cs
辉鸭蛋 a4bc632ae7 ui test
新增任务类属性和配置页面,优化日志记录

在 `BetterGenshinImpact.csproj` 文件中,添加了两个新文件夹路径 `GameTask\OneDragon\` 和 `User\AutoPathing\`。

在多个任务类文件中(如 `AutoDomainTask.cs`、`AutoFightTask.cs`、`AutoGeniusInvokationTask.cs`、`AutoMusicGameTask.cs`、`AutoWoodTask.cs`),新增了 `Name` 属性。

在 `PickAroundHandler.cs` 文件中,优化了 `RunAsync` 方法的日志记录,并添加了超时检查。

在 `ISoloTask.cs` 文件中,新增了 `Name` 属性和 `Start` 方法的接口定义。

在 `OneDragonTaskItem.cs` 文件中,新增了 `ViewModel` 属性。

在 `OneDragonFlowPage.xaml` 文件中,右侧配置部分从 `StackPanel` 改为 `ContentControl`,并添加了 `DataTemplate` 以支持不同任务类型的配置页面。

在 `IViewModel.cs` 文件中,将 `IViewModel` 接口的访问修饰符从 `internal` 改为 `public`。

在 `OneDragonFlowViewModel.cs` 文件中,初始化了任务项的 `ViewModel` 属性。

在 `ScriptControlViewModel.cs` 文件中,移除了构造函数的 `HomePageViewModel` 参数。

新增了 `LoginConfigViewModel.cs` 和 `MailConfigViewModel.cs` 文件,定义了相应的 ViewModel 类。

新增了 `LoginConfigPage.xaml` 和 `MailConfigPage.xaml` 文件,定义了相应的 XAML 布局及其交互逻辑。
2024-10-19 17:42:58 +08:00

172 lines
5.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using BetterGenshinImpact.Core.Recognition.ONNX;
using BetterGenshinImpact.GameTask.AutoFight.Model;
using BetterGenshinImpact.GameTask.AutoFight.Script;
using BetterGenshinImpact.GameTask.AutoPathing;
using BetterGenshinImpact.GameTask.Model.Area;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Helpers;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
namespace BetterGenshinImpact.GameTask.AutoFight;
public class AutoFightTask : ISoloTask
{
public string Name => "自动战斗";
private readonly AutoFightParam _taskParam;
private readonly CombatScriptBag _combatScriptBag;
private CancellationToken _ct;
private readonly BgiYoloV8Predictor _predictor;
private DateTime _lastFightFlagTime = DateTime.Now; // 战斗标志最近一次出现的时间
public AutoFightTask(AutoFightParam taskParam)
{
_taskParam = taskParam;
_combatScriptBag = CombatScriptParser.ReadAndParse(_taskParam.CombatStrategyPath);
if (_taskParam.FightFinishDetectEnabled)
{
_predictor = new BgiYoloV8Predictor(@"Assets\Model\World\bgi_world.onnx");
}
}
public async Task Start(CancellationToken ct)
{
_ct = ct;
LogScreenResolution();
var combatScenes = new CombatScenes().InitializeTeam(CaptureToRectArea());
if (!combatScenes.CheckTeamInitialized())
{
throw new Exception("识别队伍角色失败");
}
var combatCommands = _combatScriptBag.FindCombatScript(combatScenes.Avatars);
// 新的取消token
var cts2 = new CancellationTokenSource();
ct.Register(cts2.Cancel);
combatScenes.BeforeTask(cts2.Token);
// 战斗操作
var fightTask = Task.Run(() =>
{
try
{
while (!cts2.Token.IsCancellationRequested)
{
// 通用化战斗策略
foreach (var command in combatCommands)
{
command.Execute(combatScenes);
}
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
Debug.WriteLine(e.StackTrace);
}
}, cts2.Token);
// 战斗结束检测线程
var endTask = Task.Run(async () =>
{
if (!_taskParam.FightFinishDetectEnabled)
{
return;
}
try
{
while (!cts2.IsCancellationRequested)
{
var finish = await CheckFightFinish();
if (finish)
{
await cts2.CancelAsync();
break;
}
Sleep(1000, cts2.Token);
}
}
catch (Exception e)
{
Debug.WriteLine(e.Message);
Debug.WriteLine(e.StackTrace);
}
}, cts2.Token);
await Task.WhenAll(fightTask, endTask);
if (_taskParam is { FightFinishDetectEnabled: true, PickDropsAfterFightEnabled: true })
{
// 执行自动拾取掉落物的功能
}
}
private void LogScreenResolution()
{
var gameScreenSize = SystemControl.GetGameScreenRect(TaskContext.Instance().GameHandle);
if (gameScreenSize.Width * 9 != gameScreenSize.Height * 16)
{
Logger.LogWarning("游戏窗口分辨率不是 16:9 !当前分辨率为 {Width}x{Height} , 非 16:9 分辨率的游戏可能无法正常使用自动战斗功能 !", gameScreenSize.Width, gameScreenSize.Height);
}
}
private async Task<bool> CheckFightFinish()
{
// YOLO 判断血条和怪物位置
if (HasFightFlag(CaptureToRectArea()))
{
_lastFightFlagTime = DateTime.Now;
return false;
}
// 几秒内没有检测到血条和怪物位置,则开始旋转视角重新检测
if ((DateTime.Now - _lastFightFlagTime).TotalSeconds > 5)
{
// 旋转完毕后都没有检测到血条和怪物位置则按L键确认战斗结束
List<int> angles = [0, 90, 180, 270];
var rotateTask = new CameraRotateTask(_ct);
foreach (var a in angles)
{
await rotateTask.WaitUntilRotatedTo(a, 5, 30);
await Delay(1000, _ct!); // 等待视角稳定
if (HasFightFlag(CaptureToRectArea()))
{
return false;
}
}
// 最终方案确认战斗结束
Logger.LogInformation("识别到战斗结束");
return true;
}
return false;
}
private bool HasFightFlag(ImageRegion imageRegion)
{
if (RuntimeHelper.IsDebug)
{
imageRegion.SrcMat.SaveImage(Global.Absolute(@"log\fight\" + $"{DateTime.Now:yyyyMMdd_HHmmss_ffff}.png"));
}
var dict = _predictor.Detect(imageRegion);
return dict.ContainsKey("health_bar") || dict.ContainsKey("enemy_identify");
}
}