using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.ONNX;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoFight;
using BetterGenshinImpact.GameTask.AutoFight.Assets;
using BetterGenshinImpact.GameTask.AutoFight.Model;
using BetterGenshinImpact.GameTask.AutoFight.Script;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using BetterGenshinImpact.GameTask.AutoPick.Assets;
using BetterGenshinImpact.GameTask.Common.Map;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Service.Notification;
using BetterGenshinImpact.View.Drawable;
using Compunet.YoloV8;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition;
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;
using static Vanara.PInvoke.User32;
namespace BetterGenshinImpact.GameTask.AutoDomain;
public class AutoDomainTask : ISoloTask
{
public string Name => "自动秘境";
private readonly AutoDomainParam _taskParam;
private readonly YoloV8Predictor _predictor;
private readonly AutoDomainConfig _config;
private readonly CombatScriptBag _combatScriptBag;
private CancellationToken _ct;
public AutoDomainTask(AutoDomainParam taskParam)
{
AutoFightAssets.DestroyInstance();
_taskParam = taskParam;
_predictor = YoloV8Builder.CreateDefaultBuilder()
.UseOnnxModel(Global.Absolute(@"Assets\Model\Domain\bgi_tree.onnx"))
.WithSessionOptions(BgiSessionOption.Instance.Options)
.Build();
_config = TaskContext.Instance().Config.AutoDomainConfig;
_combatScriptBag = CombatScriptParser.ReadAndParse(_taskParam.CombatStrategyPath);
}
public async Task Start(CancellationToken ct)
{
_ct = ct;
Init();
Notify.Event(NotificationEvent.DomainStart).Success("自动秘境启动");
// 3次复活重试
for (int i = 0; i < 3; i++)
{
try
{
await DoDomain();
// 其他场景不重试
break;
}
catch (Exception e)
{
if (e.Message.Contains("复活") && !string.IsNullOrEmpty(_taskParam.DomainName))
{
Logger.LogWarning("自动秘境:{Text}", "复活后重试秘境...");
await Delay(2000, ct);
Notify.Event(NotificationEvent.DomainRetry).Error("存在角色死亡,复活后重试秘境...");
continue;
}
else
{
throw;
}
}
}
await Delay(2000, ct);
await Bv.WaitForMainUi(_ct, 30);
await Delay(2000, ct);
await ArtifactSalvage();
Notify.Event(NotificationEvent.DomainEnd).Success("自动秘境结束");
}
private async Task DoDomain()
{
// 传送到秘境
await TpDomain();
// 切换队伍
await SwitchParty(_taskParam.PartyName);
var combatScenes = new CombatScenes().InitializeTeam(CaptureToRectArea());
// 前置进入秘境
await EnterDomain();
for (var i = 0; i < _taskParam.DomainRoundNum; i++)
{
// 0. 关闭秘境提示
Logger.LogDebug("0. 关闭秘境提示");
await CloseDomainTip();
// 队伍没初始化成功则重试
RetryTeamInit(combatScenes);
// 0. 切换到第一个角色
var combatCommands = FindCombatScriptAndSwitchAvatar(combatScenes);
// 1. 走到钥匙处启动
Logger.LogInformation("自动秘境:{Text}", "1. 走到钥匙处启动");
await WalkToPressF();
// 2. 执行战斗(战斗线程、视角线程、检测战斗完成线程)
Logger.LogInformation("自动秘境:{Text}", "2. 执行战斗策略");
await StartFight(combatScenes, combatCommands);
combatScenes.AfterTask();
EndFightWait();
// 3. 寻找石化古树 并左右移动直到石化古树位于屏幕中心
Logger.LogInformation("自动秘境:{Text}", "3. 寻找石化古树");
await FindPetrifiedTree();
// 4. 走到石化古树处
Logger.LogInformation("自动秘境:{Text}", "4. 走到石化古树处");
await WalkToPressF();
// 5. 快速领取奖励并判断是否有下一轮
Logger.LogInformation("自动秘境:{Text}", "5. 领取奖励");
if (!GettingTreasure(_taskParam.DomainRoundNum == 9999, i == _taskParam.DomainRoundNum - 1))
{
if (i == _taskParam.DomainRoundNum - 1)
{
Logger.LogInformation("配置的{Cnt}轮秘境已经完成,结束自动秘境", _taskParam.DomainRoundNum);
}
else
{
Logger.LogInformation("体力已经耗尽,结束自动秘境");
}
break;
}
Notify.Event(NotificationEvent.DomainReward).Success("自动秘境奖励领取");
}
}
private void Init()
{
LogScreenResolution();
if (_taskParam.DomainRoundNum == 9999)
{
Logger.LogInformation("→ {Text} 用尽所有体力后结束", "自动秘境,");
}
else
{
Logger.LogInformation("→ {Text} 设置总次数:{Cnt}", "自动秘境,", _taskParam.DomainRoundNum);
}
}
private void LogScreenResolution()
{
var gameScreenSize = SystemControl.GetGameScreenRect(TaskContext.Instance().GameHandle);
if (gameScreenSize.Width * 9 != gameScreenSize.Height * 16)
{
Logger.LogError("游戏窗口分辨率不是 16:9 !当前分辨率为 {Width}x{Height} , 非 16:9 分辨率的游戏无法正常使用自动秘境功能 !", gameScreenSize.Width, gameScreenSize.Height);
throw new Exception("游戏窗口分辨率不是 16:9");
}
if (gameScreenSize.Width < 1920 || gameScreenSize.Height < 1080)
{
Logger.LogWarning("游戏窗口分辨率小于 1920x1080 !当前分辨率为 {Width}x{Height} , 小于 1920x1080 的分辨率的游戏可能无法正常使用自动秘境功能 !", gameScreenSize.Width, gameScreenSize.Height);
}
}
private void RetryTeamInit(CombatScenes combatScenes)
{
if (!combatScenes.CheckTeamInitialized())
{
combatScenes.InitializeTeam(CaptureToRectArea());
if (!combatScenes.CheckTeamInitialized())
{
throw new Exception("识别队伍角色失败,请在较暗背景下重试,比如游戏时间调整成夜晚。或者直接使用强制指定当前队伍角色的功能。");
}
}
}
private async Task TpDomain()
{
// 传送到秘境
if (!string.IsNullOrEmpty(_taskParam.DomainName))
{
if (MapLazyAssets.Instance.DomainPositionMap.TryGetValue(_taskParam.DomainName, out var domainPosition))
{
Logger.LogInformation("自动秘境:传送到秘境{Text}", _taskParam.DomainName);
await new TpTask(_ct).Tp(domainPosition.X, domainPosition.Y);
await Delay(1000, _ct);
await Bv.WaitForMainUi(_ct);
await Delay(1000, _ct);
if ("芬德尼尔之顶".Equals(_taskParam.DomainName))
{
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyDown);
Thread.Sleep(3000);
Simulation.SendInput.SimulateAction(GIActions.MoveBackward, KeyType.KeyUp);
}
else if ("无妄引咎密宫".Equals(_taskParam.DomainName))
{
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Thread.Sleep(500);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
Thread.Sleep(100);
Simulation.SendInput.SimulateAction(GIActions.MoveLeft, KeyType.KeyDown);
Thread.Sleep(1600);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
else if ("苍白的遗荣".Equals(_taskParam.DomainName))
{
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Thread.Sleep(1000);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
else if ("太山府".Equals(_taskParam.DomainName))
{
// 直接F即可
// nothing to do
}
else
{
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Thread.Sleep(2000);
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
}
Simulation.SendInput.SimulateAction(GIActions.Drop, KeyType.KeyUp); // 可能爬上去了,X键下来
await Delay(3000, _ct); // 站稳
}
else
{
Logger.LogError("自动秘境:未找到对应的秘境{Text}的传送点", _taskParam.DomainName);
throw new Exception($"未找到对应的秘境{_taskParam.DomainName}的传送点");
}
}
}
///
/// 切换队伍
///
///
///
private async Task SwitchParty(string? partyName)
{
if (!string.IsNullOrEmpty(partyName))
{
var b = await new SwitchPartyTask().Start(partyName, _ct);
await Delay(500, _ct);
return b;
}
return true;
}
private async Task EnterDomain()
{
var fightAssets = AutoFightAssets.Instance;
// 进入秘境
for (int i = 0; i < 3; i++) // 3次重试 有时候会拾取晶蝶
{
using var fRectArea = CaptureToRectArea().Find(AutoPickAssets.Instance.PickRo);
if (!fRectArea.IsEmpty())
{
Simulation.SendInput.Keyboard.KeyPress(AutoPickAssets.Instance.PickVk);
Logger.LogInformation("自动秘境:{Text}", "进入秘境");
// 秘境开门动画 5s
await Delay(5000, _ct);
}
else
{
await Delay(800, _ct);
}
}
int retryTimes = 0, clickCount = 0;
while (retryTimes < 20 && clickCount < 2)
{
retryTimes++;
using var confirmRectArea = CaptureToRectArea().Find(fightAssets.ConfirmRa);
if (!confirmRectArea.IsEmpty())
{
confirmRectArea.Click();
clickCount++;
}
await Delay(1500, _ct);
}
// 载入动画
await Delay(3000, _ct);
}
private async Task CloseDomainTip()
{
// 2min的载入时间总够了吧
var retryTimes = 0;
while (retryTimes < 120)
{
retryTimes++;
using var cactRectArea = CaptureToRectArea().Find(AutoFightAssets.Instance.ClickAnyCloseTipRa);
if (!cactRectArea.IsEmpty())
{
await Delay(1000, _ct);
cactRectArea.Click();
break;
}
// todo 添加小地图角标位置检测 防止有人手点了
await Delay(1000, _ct);
}
await Delay(2000, _ct);
}
private List FindCombatScriptAndSwitchAvatar(CombatScenes combatScenes)
{
var combatCommands = _combatScriptBag.FindCombatScript(combatScenes.Avatars);
var avatar = combatScenes.SelectAvatar(combatCommands[0].Name);
avatar?.SwitchWithoutCts();
Sleep(200);
return combatCommands;
}
///
/// 走到钥匙处启动
///
private async Task WalkToPressF()
{
if (_ct.IsCancellationRequested)
{
return;
}
await Task.Run((Action)(() =>
{
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown);
Sleep(30, _ct);
// 组合键好像不能直接用 postmessage
if (!_config.WalkToF)
{
Simulation.SendInput.SimulateAction(GIActions.SprintKeyboard, KeyType.KeyDown);
}
try
{
while (!_ct.IsCancellationRequested)
{
using var fRectArea = Common.TaskControl.CaptureToRectArea().Find(AutoPickAssets.Instance.PickRo);
if (fRectArea.IsEmpty())
{
Sleep(100, _ct);
}
else
{
Logger.LogInformation("检测到交互键");
Simulation.SendInput.Keyboard.KeyPress(AutoPickAssets.Instance.PickVk);
break;
}
}
}
finally
{
Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp);
Sleep(50);
if (!_config.WalkToF)
{
Simulation.SendInput.SimulateAction(GIActions.SprintKeyboard, KeyType.KeyUp);
}
}
}), _ct);
}
private Task StartFight(CombatScenes combatScenes, List combatCommands)
{
CancellationTokenSource cts = new();
_ct.Register(cts.Cancel);
combatScenes.BeforeTask(cts.Token);
// 战斗操作
var combatTask = new Task(() =>
{
try
{
while (!cts.Token.IsCancellationRequested)
{
// 通用化战斗策略
foreach (var command in combatCommands)
{
command.Execute(combatScenes);
}
}
}
catch (NormalEndException e)
{
Logger.LogInformation("战斗操作中断:{Msg}", e.Message);
}
catch (Exception e)
{
Logger.LogWarning(e.Message);
throw;
}
finally
{
Logger.LogInformation("自动战斗线程结束");
}
}, cts.Token);
// 对局结束检测
var domainEndTask = DomainEndDetectionTask(cts);
// 自动吃药
var autoEatRecoveryHpTask = AutoEatRecoveryHpTask(cts.Token);
combatTask.Start();
domainEndTask.Start();
autoEatRecoveryHpTask.Start();
return Task.WhenAll(combatTask, domainEndTask, autoEatRecoveryHpTask);
}
private void EndFightWait()
{
if (_ct.IsCancellationRequested)
{
return;
}
var s = TaskContext.Instance().Config.AutoDomainConfig.FightEndDelay;
if (s > 0)
{
Logger.LogInformation("战斗结束后等待 {Second} 秒", s);
Sleep((int)(s * 1000), _ct);
}
}
///
/// 对局结束检测
///
private Task DomainEndDetectionTask(CancellationTokenSource cts)
{
return new Task(async () =>
{
try
{
while (!_ct.IsCancellationRequested)
{
if (IsDomainEnd())
{
await cts.CancelAsync();
break;
}
await Delay(1000, cts.Token);
}
}
catch
{
}
}, cts.Token);
}
private bool IsDomainEnd()
{
using var ra = CaptureToRectArea();
var endTipsRect = ra.DeriveCrop(AutoFightAssets.Instance.EndTipsUpperRect);
var text = OcrFactory.Paddle.Ocr(endTipsRect.SrcGreyMat);
if (text.Contains("挑战") || text.Contains("达成"))
{
Logger.LogInformation("检测到秘境结束提示(挑战达成),结束秘境");
return true;
}
endTipsRect = ra.DeriveCrop(AutoFightAssets.Instance.EndTipsRect);
text = OcrFactory.Paddle.Ocr(endTipsRect.SrcGreyMat);
if (text.Contains("自动") || text.Contains("退出"))
{
Logger.LogInformation("检测到秘境结束提示(xxx秒后自动退出),结束秘境");
return true;
}
return false;
}
private Task AutoEatRecoveryHpTask(CancellationToken ct)
{
return new Task(async () =>
{
if (!_config.AutoEat)
{
return;
}
if (!IsTakeFood())
{
Logger.LogInformation("未装备 “{Tool}”,不启用红血自动吃药功能", "便携营养袋");
return;
}
try
{
while (!_ct.IsCancellationRequested)
{
if (Bv.CurrentAvatarIsLowHp(CaptureToRectArea()))
{
// 模拟按键 "Z"
Simulation.SendInput.SimulateAction(GIActions.QuickUseGadget);
Logger.LogInformation("检测到红血,按Z吃药");
// TODO 吃饱了会一直吃
}
await Delay(500, ct);
}
}
catch (Exception e)
{
Logger.LogDebug(e, "红血自动吃药检测时发生异常");
}
}, ct);
}
private bool IsTakeFood()
{
// 获取图像
using var ra = CaptureToRectArea();
// 识别道具图标下是否是数字
var s = TaskContext.Instance().SystemInfo.AssetScale;
var countArea = ra.DeriveCrop(1800 * s, 845 * s, 40 * s, 20 * s);
var count = OcrFactory.Paddle.OcrWithoutDetector(countArea.SrcGreyMat);
return int.TryParse(count, out _);
}
///
/// 旋转视角后寻找石化古树
///
private Task FindPetrifiedTree()
{
CancellationTokenSource treeCts = new();
_ct.Register(treeCts.Cancel);
// 中键回正视角
Simulation.SendInput.Mouse.MiddleButtonClick();
Sleep(900, _ct);
// 左右移动直到石化古树位于屏幕中心任务
var moveAvatarTask = MoveAvatarHorizontallyTask(treeCts);
// 锁定东方向视角线程
var lockCameraToEastTask = LockCameraToEastTask(treeCts, moveAvatarTask);
lockCameraToEastTask.Start();
return Task.WhenAll(moveAvatarTask, lockCameraToEastTask);
}
private Task MoveAvatarHorizontallyTask(CancellationTokenSource treeCts)
{
return new Task(() =>
{
var keyConfig = TaskContext.Instance().Config.KeyBindingsConfig;
var moveLeftKey = keyConfig.MoveLeft.ToVK();
var moveRightKey = keyConfig.MoveRight.ToVK();
var moveForwardKey = keyConfig.MoveForward.ToVK();
var captureArea = TaskContext.Instance().SystemInfo.ScaleMax1080PCaptureRect;
var middleX = captureArea.Width / 2;
var leftKeyDown = false;
var rightKeyDown = false;
var noDetectCount = 0;
var prevKey = moveLeftKey;
var backwardsAndForwardsCount = 0;
while (!_ct.IsCancellationRequested)
{
var treeRect = DetectTree(CaptureToRectArea());
if (treeRect != Rect.Empty)
{
var treeMiddleX = treeRect.X + treeRect.Width / 2;
if (treeRect.X + treeRect.Width < middleX && !_config.ShortMovement)
{
backwardsAndForwardsCount = 0;
// 树在左边 往左走
Debug.WriteLine($"树在左边 往左走 {treeMiddleX} {middleX}");
if (rightKeyDown)
{
// 先松开D键
Simulation.SendInput.Keyboard.KeyUp(moveRightKey);
rightKeyDown = false;
}
if (!leftKeyDown)
{
Simulation.SendInput.Keyboard.KeyDown(moveLeftKey);
leftKeyDown = true;
}
}
else if (treeRect.X > middleX && !_config.ShortMovement)
{
backwardsAndForwardsCount = 0;
// 树在右边 往右走
Debug.WriteLine($"树在右边 往右走 {treeMiddleX} {middleX}");
if (leftKeyDown)
{
// 先松开A键
Simulation.SendInput.Keyboard.KeyUp(moveLeftKey);
leftKeyDown = false;
}
if (!rightKeyDown)
{
Simulation.SendInput.Keyboard.KeyDown(moveRightKey);
rightKeyDown = true;
}
}
else
{
// 树在中间 松开所有键
if (rightKeyDown)
{
Simulation.SendInput.Keyboard.KeyUp(moveRightKey);
prevKey = moveRightKey;
rightKeyDown = false;
}
if (leftKeyDown)
{
Simulation.SendInput.Keyboard.KeyUp(moveLeftKey);
prevKey = moveLeftKey;
leftKeyDown = false;
}
// 松开按键后使用小碎步移动
if (treeMiddleX < middleX)
{
if (prevKey == moveRightKey)
{
backwardsAndForwardsCount++;
}
Simulation.SendInput.Keyboard.KeyDown(moveLeftKey);
Sleep(60);
Simulation.SendInput.Keyboard.KeyUp(moveLeftKey);
prevKey = moveLeftKey;
}
else if (treeMiddleX > middleX)
{
if (prevKey == moveLeftKey)
{
backwardsAndForwardsCount++;
}
Simulation.SendInput.Keyboard.KeyDown(moveRightKey);
Sleep(60);
Simulation.SendInput.Keyboard.KeyUp(moveRightKey);
prevKey = moveRightKey;
}
else
{
Simulation.SendInput.Keyboard.KeyDown(moveForwardKey);
Sleep(60);
Simulation.SendInput.Keyboard.KeyUp(moveForwardKey);
Sleep(500, _ct);
treeCts.Cancel();
break;
}
}
}
else
{
backwardsAndForwardsCount = 0;
// 左右巡逻
noDetectCount++;
if (noDetectCount > 40)
{
if (leftKeyDown)
{
Simulation.SendInput.Keyboard.KeyUp(moveLeftKey);
leftKeyDown = false;
}
if (!rightKeyDown)
{
Simulation.SendInput.Keyboard.KeyDown(moveRightKey);
rightKeyDown = true;
}
}
else
{
if (rightKeyDown)
{
Simulation.SendInput.Keyboard.KeyUp(moveRightKey);
rightKeyDown = false;
}
if (!leftKeyDown)
{
Simulation.SendInput.Keyboard.KeyDown(moveLeftKey);
leftKeyDown = true;
}
}
}
if (backwardsAndForwardsCount >= _config.LeftRightMoveTimes)
{
// 左右移动5次说明已经在树中心了
Simulation.SendInput.Keyboard.KeyDown(moveForwardKey);
Sleep(60);
Simulation.SendInput.Keyboard.KeyUp(moveForwardKey);
Sleep(500, _ct);
treeCts.Cancel();
break;
}
Sleep(60, _ct);
}
VisionContext.Instance().DrawContent.ClearAll();
});
}
private Rect DetectTree(ImageRegion region)
{
using var memoryStream = new MemoryStream();
region.SrcBitmap.Save(memoryStream, ImageFormat.Bmp);
memoryStream.Seek(0, SeekOrigin.Begin);
var result = _predictor.Detect(memoryStream);
var list = new List();
foreach (var box in result.Boxes)
{
var rect = new Rect(box.Bounds.X, box.Bounds.Y, box.Bounds.Width, box.Bounds.Height);
list.Add(region.ToRectDrawable(rect, "tree"));
}
VisionContext.Instance().DrawContent.PutOrRemoveRectList("TreeBox", list);
if (list.Count > 0)
{
var box = result.Boxes[0];
return new Rect(box.Bounds.X, box.Bounds.Y, box.Bounds.Width, box.Bounds.Height);
}
return Rect.Empty;
}
private Task LockCameraToEastTask(CancellationTokenSource cts, Task moveAvatarTask)
{
return new Task(() =>
{
var continuousCount = 0; // 连续东方向次数
var started = false;
while (!cts.Token.IsCancellationRequested)
{
using var captureRegion = CaptureToRectArea();
var angle = CameraOrientation.Compute(captureRegion.SrcMat);
CameraOrientation.DrawDirection(captureRegion, angle);
if (angle is >= 356 or <= 4)
{
// 算作对准了
continuousCount++;
// 360 度 东方向视角
if (continuousCount > 5)
{
if (!started && moveAvatarTask.Status != TaskStatus.Running)
{
started = true;
moveAvatarTask.Start();
}
}
}
else
{
continuousCount = 0;
}
if (angle <= 180)
{
// 左移视角
var moveAngle = (int)Math.Round(angle);
if (moveAngle > 2)
{
moveAngle *= 2;
}
Simulation.SendInput.Mouse.MoveMouseBy(-moveAngle, 0);
}
else if (angle is > 180 and < 360)
{
// 右移视角
var moveAngle = 360 - (int)Math.Round(angle);
if (moveAngle > 2)
{
moveAngle *= 2;
}
Simulation.SendInput.Mouse.MoveMouseBy(moveAngle, 0);
}
Sleep(100);
}
Logger.LogInformation("锁定东方向视角线程结束");
VisionContext.Instance().DrawContent.ClearAll();
});
}
///
/// 领取奖励
///
/// 是否识别树脂
/// 是否最后一轮
private bool GettingTreasure(bool recognizeResin, bool isLastTurn)
{
// 等待窗口弹出
Sleep(1500, _ct);
// 优先使用浓缩树脂
var retryTimes = 0;
while (true)
{
retryTimes++;
if (retryTimes > 3)
{
Logger.LogInformation("没有浓缩树脂了");
break;
}
var useCondensedResinRa = CaptureToRectArea().Find(AutoFightAssets.Instance.UseCondensedResinRa);
if (!useCondensedResinRa.IsEmpty())
{
useCondensedResinRa.Click();
// 点两下 #224 #218
// 解决水龙王按下左键后没松开,然后后续点击按下就没反应了
Sleep(400, _ct);
useCondensedResinRa.Click();
break;
}
Sleep(800, _ct);
}
Sleep(1000, _ct);
var hasSkip = false;
var captureArea = TaskContext.Instance().SystemInfo.ScaleMax1080PCaptureRect;
var assetScale = TaskContext.Instance().SystemInfo.AssetScale;
for (var i = 0; i < 30; i++)
{
// 跳过领取动画
if (!hasSkip)
{
TaskContext.Instance().PostMessageSimulator.LeftButtonClick(); // 先随便点一个地方使得跳过出现
}
using var ra = CaptureToRectArea();
// OCR识别是否有跳过
var ocrList = ra.FindMulti(RecognitionObject.Ocr(captureArea.Width - 230 * assetScale, 0, 230 * assetScale - 5, 80 * assetScale));
var skipTextRa = ocrList.FirstOrDefault(t => t.Text.Contains("跳过"));
if (skipTextRa != null)
{
hasSkip = true;
skipTextRa.Click(); // 有则点击
}
// 优先点击继续
using var confirmRectArea = ra.Find(AutoFightAssets.Instance.ConfirmRa);
if (!confirmRectArea.IsEmpty())
{
if (isLastTurn)
{
// 最后一回合 退出
var exitRectArea = ra.Find(AutoFightAssets.Instance.ExitRa);
if (!exitRectArea.IsEmpty())
{
exitRectArea.Click();
return false;
}
}
if (!recognizeResin)
{
confirmRectArea.Click();
return true;
}
var (condensedResinCount, fragileResinCount) = GetRemainResinStatus();
if (condensedResinCount == 0 && fragileResinCount < 20)
{
// 没有体力了退出
var exitRectArea = ra.Find(AutoFightAssets.Instance.ExitRa);
if (!exitRectArea.IsEmpty())
{
exitRectArea.Click();
return false;
}
}
else
{
// 有体力继续
confirmRectArea.Click();
return true;
}
}
Sleep(300, _ct);
}
throw new NormalEndException("未检测到秘境结束,可能是背包物品已满。");
}
///
/// 获取剩余树脂状态
///
private (int, int) GetRemainResinStatus()
{
var condensedResinCount = 0;
var fragileResinCount = 0;
var ra = CaptureToRectArea();
// 浓缩树脂
var condensedResinCountRa = ra.Find(AutoFightAssets.Instance.CondensedResinCountRa);
if (!condensedResinCountRa.IsEmpty())
{
// 图像右侧就是浓缩树脂数量
var countArea = ra.DeriveCrop(condensedResinCountRa.X + condensedResinCountRa.Width, condensedResinCountRa.Y, condensedResinCountRa.Width, condensedResinCountRa.Height);
// Cv2.ImWrite($"log/resin_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff")}.png", countArea.SrcGreyMat);
var count = OcrFactory.Paddle.OcrWithoutDetector(countArea.SrcGreyMat);
condensedResinCount = StringUtils.TryParseInt(count);
}
// 脆弱树脂
var fragileResinCountRa = ra.Find(AutoFightAssets.Instance.FragileResinCountRa);
if (!fragileResinCountRa.IsEmpty())
{
// 图像右侧就是脆弱树脂数量
var countArea = ra.DeriveCrop(fragileResinCountRa.X + fragileResinCountRa.Width, fragileResinCountRa.Y, (int)(fragileResinCountRa.Width * 3), fragileResinCountRa.Height);
var count = OcrFactory.Paddle.Ocr(countArea.SrcGreyMat);
fragileResinCount = StringUtils.TryParseInt(count);
}
Logger.LogInformation("剩余:浓缩树脂 {CondensedResinCount} 脆弱树脂 {FragileResinCount}", condensedResinCount, fragileResinCount);
return (condensedResinCount, fragileResinCount);
}
private async Task ArtifactSalvage()
{
if (!_taskParam.AutoArtifactSalvage)
{
return;
}
if (!int.TryParse(_taskParam.MaxArtifactStar, out var star))
{
star = 4;
}
await new ArtifactSalvageTask().Start(star, _ct);
}
}