Files
better-genshin-impact/BetterGenshinImpact/GameTask/Common/Job/SwitchPartyTask.cs
FishmanTheMurloc fce70c0e96 分解5星圣遗物 (#1383)
* 分解圣遗物基础设施建设

* 分解圣遗物独立任务基本功能完成:单独的启动按钮,正则表达式逐一筛选;代码文件整理到单独的文件夹

* 自动分解5星圣遗物功能初步完成

* 修复上次修改快速分解产生的问题,主要点击分解按钮时的bug,还有与五星分解步骤衔接的问题

* 针对切换队伍时,多语言识别效果不佳的情况,将用户设定的队伍名作为正则表达式进行模糊匹配,并在LogInfo输出相关提示;传送任务对任务取消进行单独的异常处理

* 一个便于测试分解圣遗物OCR识别和正则匹配结果的弹窗
2025-04-05 19:53:52 +08:00

189 lines
6.7 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;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Model.Area;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.Common.Exceptions;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using System.Text.RegularExpressions;
namespace BetterGenshinImpact.GameTask.Common.Job;
public class SwitchPartyTask
{
private readonly double _assetScale = TaskContext.Instance().SystemInfo.AssetScale;
public string Name => "切换队伍";
private readonly ReturnMainUiTask _returnMainUiTask = new();
public async Task<bool> Start(string partyName, CancellationToken ct)
{
Logger.LogInformation("尝试切换至队伍: {Name}", partyName);
using var ra1 = CaptureToRectArea();
if (!Bv.IsInPartyViewUi(ra1))
{
// 如果不在主界面,则返回主界面
if (!Bv.IsInMainUi(ra1))
{
await _returnMainUiTask.Start(ct);
await Delay(200, ct);
using var raAfterMain = CaptureToRectArea();
if (!Bv.IsInMainUi(raAfterMain))
{
throw new InvalidOperationException("未能返回主界面");
}
}
// 尝试打开队伍配置页面
Simulation.SendInput.SimulateAction(GIActions.OpenPartySetupScreen);
// 考虑加载时间 2s共检查 5s如果失败则抛出异常
bool isOpened = false;
for (int i = 0; i < 5; i++) // 检查 5 次
{
await Delay(1000, ct);
using var raCheck = CaptureToRectArea();
if (Bv.IsInPartyViewUi(raCheck))
{
isOpened = true;
break;
}
}
if (!isOpened)
{
throw new PartySetupFailedException("未能打开队伍配置界面");
}
}
await Delay(500, ct);
using var ra = CaptureToRectArea();
var partyViewBtn = ra.Find(ElementAssets.Instance.PartyBtnChooseView);
// OCR 当前队伍名称(无法单字,中间禁止空格)
var currTeamName = ra.Find(new RecognitionObject
{
RecognitionType = RecognitionTypes.Ocr,
RegionOfInterest = new Rect(partyViewBtn.Right, partyViewBtn.Top, (int)(350 * _assetScale),
partyViewBtn.Height)
}).Text;
Logger.LogInformation("切换队伍,当前队伍名称: {Text},使用正则表达式规则进行模糊匹配", currTeamName);
if (Regex.IsMatch(currTeamName, partyName))
{
Logger.LogInformation("当前队伍[{Name}]即为目标队伍,无需切换", currTeamName);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_ESCAPE);
await Delay(500, ct);
await _returnMainUiTask.Start(ct);
return true;
}
// 点击队伍选择按钮
partyViewBtn.Click();
await Delay(500, ct);
ImageRegion? switchRa = null;
Region? partyDeleteBtn = null;
using (var ocrRa = CaptureToRectArea())
{
var openPartyChooseSuccess = await NewRetry.WaitForAction(() =>
{
switchRa = ocrRa;
partyDeleteBtn = switchRa.Find(ElementAssets.Instance.PartyBtnDelete);
return partyDeleteBtn.IsExist();
}, ct, 5);
if (!openPartyChooseSuccess || switchRa == null || partyDeleteBtn == null)
{
throw new PartySetupFailedException("未能打开队伍配置界面");
}
}
var nextX = partyDeleteBtn.Left;
var nextY = partyDeleteBtn.Top - partyDeleteBtn.Height * 2;
// 滚轮到最上方
switchRa.MoveTo(nextX, nextY);
await Delay(10, ct);
for (var i = 0; i < 100; i++)
{
Simulation.SendInput.Mouse.VerticalScroll(1);
await Delay(10, ct);
}
await Delay(200, ct);
// 逐页查找
for (var i = 0; i < 11; i++)
{
using var page = CaptureToRectArea();
var found = await FindPage(partyName, page, partyDeleteBtn, ct);
if (found)
{
return true;
}
// 点击下一页
if (i == 0)
{
// #ebe4d8 首次点一下第一个,防止第五个被点击过
page.ClickTo(600 * _assetScale, 200 * _assetScale);
}
page.ClickTo(nextX, nextY); // 点击最下方队伍下移
await Delay(400, ct);
}
// 未找到
Logger.LogError("未找到队伍: {Name},返回主界面", partyName);
Logger.LogInformation("如果找不到设定的队伍名,有可能是文字识别效果不佳,请尝试正则表达式");
await _returnMainUiTask.Start(ct);
return false;
}
private async Task<bool> FindPage(string partyName, ImageRegion page, Region partyDeleteBtn, CancellationToken ct)
{
var partySwitchNameRaList = page.FindMulti(new RecognitionObject
{
RecognitionType = RecognitionTypes.Ocr,
RegionOfInterest = new Rect(0, (int)(80 * _assetScale), partyDeleteBtn.Right, partyDeleteBtn.Top - (int)(80 * _assetScale))
});
// 当前页存在则直接点击
foreach (var textRegion in partySwitchNameRaList)
{
if (Regex.IsMatch(textRegion.Text, partyName))
{
page.ClickTo(textRegion.Right + textRegion.Width, textRegion.Bottom);
await Delay(200, ct);
Logger.LogInformation("切换队伍成功: {Text}", textRegion.Text);
await ConfirmParty(page, ct);
return true;
}
}
return false;
}
private async Task ConfirmParty(ImageRegion page, CancellationToken ct)
{
var r1 = Bv.ClickWhiteConfirmButton(page.DeriveCrop(0, page.Height / 4, page.Width / 4, page.Height - page.Height / 4));
await Delay(1000, ct);
using var ra = CaptureToRectArea();
var r2 = Bv.ClickWhiteConfirmButton(ra.DeriveCrop(page.Width - page.Width / 4, page.Height / 4, page.Width / 4, page.Height - page.Height / 4));
await Delay(500, ct);
await _returnMainUiTask.Start(ct);
}
}