using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.AutoFight.Config;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Helpers;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using BetterGenshinImpact.GameTask.AutoTrackPath;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
namespace BetterGenshinImpact.GameTask.AutoFight.Model;
///
/// 队伍内的角色
///
public class Avatar
{
///
/// 角色名称 中文
///
public string Name { get; set; }
///
/// 角色名称 英文
///
public string? NameEn { get; set; }
///
/// 队伍内序号
///
public int Index { get; set; }
///
/// 武器类型
///
public string Weapon { get; set; }
///
/// 元素战技CD
///
public double SkillCd { get; set; }
///
/// 长按元素战技CD
///
public double SkillHoldCd { get; set; }
///
/// 最近一次使用元素战技的时间
///
public DateTime LastSkillTime { get; set; }
///
/// 元素爆发CD
///
public double BurstCd { get; set; }
///
/// 元素爆发是否就绪
///
public bool IsBurstReady { get; set; }
///
/// 名字所在矩形位置
///
public Rect NameRect { get; set; }
///
/// 名字右边的编号位置
///
public Rect IndexRect { get; set; }
///
/// 任务取消令牌
///
public CancellationToken Ct { get; set; }
///
/// 战斗场景
///
public CombatScenes CombatScenes { get; set; }
public Avatar(CombatScenes combatScenes, string name, int index, Rect nameRect)
{
CombatScenes = combatScenes;
Name = name;
Index = index;
NameRect = nameRect;
var ca = DefaultAutoFightConfig.CombatAvatarMap[name];
NameEn = ca.NameEn;
Weapon = ca.Weapon;
SkillCd = ca.SkillCd;
SkillHoldCd = ca.SkillHoldCd;
BurstCd = ca.BurstCd;
}
///
/// 是否存在角色被击败
/// 通过判断确认按钮
///
///
///
public void ThrowWhenDefeated(ImageRegion region)
{
if (Bv.IsInRevivePrompt(region))
{
Logger.LogWarning("检测到复苏界面,存在角色被击败,前往七天神像复活");
// 先打开地图
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_ESCAPE);
Sleep(600, Ct);
Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_M);
// tp 到七天神像复活
var tpTask = new TpTask(Ct);
tpTask.Tp(TpTask.ReviveStatueOfTheSevenPointX, TpTask.ReviveStatueOfTheSevenPointY, true).Wait(Ct);
throw new Exception("检测到复苏界面,存在角色被击败,前往七天神像复活");
}
}
///
/// 切换到本角色
/// 切换cd是1秒,如果切换失败,会尝试再次切换,最多尝试5次
///
public void Switch()
{
for (var i = 0; i < 30; i++)
{
if (Ct is { IsCancellationRequested: true })
{
return;
}
var region = CaptureToRectArea();
ThrowWhenDefeated(region);
var notActiveCount = CombatScenes.Avatars.Count(avatar => !avatar.IsActive(region));
if (IsActive(region) && notActiveCount == CombatScenes.ExpectedTeamAvatarNum - 1)
{
return;
}
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_1 + (byte)Index - 1);
// Debug.WriteLine($"切换到{Index}号位");
// Cv2.ImWrite($"log/切换.png", region.SrcMat);
Sleep(250, Ct);
}
}
///
/// 尝试切换到本角色
///
///
///
///
public bool TrySwitch(int tryTimes = 4, bool needLog = true)
{
for (var i = 0; i < 3; i++)
{
if (Ct is { IsCancellationRequested: true })
{
return false;
}
var region = CaptureToRectArea();
ThrowWhenDefeated(region);
var notActiveCount = CombatScenes.Avatars.Count(avatar => !avatar.IsActive(region));
if (IsActive(region) && notActiveCount == CombatScenes.ExpectedTeamAvatarNum - 1)
{
if (needLog && i > 0)
{
Logger.LogInformation("成功切换角色:{Name}", Name);
}
return true;
}
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_1 + (byte)Index - 1);
Sleep(250, Ct);
}
return false;
}
///
/// 切换到本角色
/// 切换cd是1秒,如果切换失败,会尝试再次切换,最多尝试5次
///
public void SwitchWithoutCts()
{
for (var i = 0; i < 10; i++)
{
var region = CaptureToRectArea();
ThrowWhenDefeated(region);
var notActiveCount = CombatScenes.Avatars.Count(avatar => !avatar.IsActive(region));
if (IsActive(region) && notActiveCount == 3)
{
return;
}
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_1 + (byte)Index - 1);
Sleep(250);
}
}
///
/// 是否出战状态
///
///
public bool IsActive(ImageRegion region)
{
if (IndexRect == Rect.Empty)
{
throw new Exception("IndexRect为空");
}
else
{
// 剪裁出IndexRect区域
var indexRa = region.DeriveCrop(IndexRect);
// Cv2.ImWrite($"log/indexRa_{Name}.png", indexRa.SrcMat);
var count = OpenCvCommonHelper.CountGrayMatColor(indexRa.SrcGreyMat, 251, 255);
if (count * 1.0 / (IndexRect.Width * IndexRect.Height) > 0.5)
{
return false;
}
else
{
return true;
}
}
}
///
/// 是否出战状态
///
///
[Obsolete]
public bool IsActiveNoIndexRect(ImageRegion region)
{
// 通过寻找右侧人物编号来判断是否出战
if (IndexRect == Rect.Empty)
{
var assetScale = TaskContext.Instance().SystemInfo.AssetScale;
// 剪裁出队伍区域
var teamRa = region.DeriveCrop(AutoFightContext.Instance.FightAssets.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);
// 取白色区域
var bMat = OpenCvCommonHelper.Threshold(block.SrcMat, new Scalar(255, 255, 255), new Scalar(255, 255, 255));
// Cv2.ImWrite($"block_b_{Name}.png", bMat);
// 矩形识别
Cv2.FindContours(bMat, out var contours, out _, RetrievalModes.External,
ContourApproximationModes.ApproxSimple);
if (contours.Length > 0)
{
var boxes = contours.Select(Cv2.BoundingRect).Where(w => w.Width >= 20 * assetScale && w.Height >= 18 * assetScale).OrderByDescending(w => w.Width).ToList();
if (boxes.Count is not 0)
{
IndexRect = boxes.First();
return false;
}
}
}
else
{
// 剪裁出IndexRect区域
var teamRa = region.DeriveCrop(AutoFightContext.Instance.FightAssets.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);
var count = OpenCvCommonHelper.CountGrayMatColor(indexBlock.SrcGreyMat, 255);
if (count * 1.0 / (IndexRect.Width * IndexRect.Height) > 0.5)
{
return false;
}
}
Logger.LogInformation("{Name} 当前出战", Name);
return true;
}
///
/// 普通攻击
///
/// 攻击时长,建议是200的倍数
public void Attack(int ms = 0)
{
while (ms >= 0)
{
if (Ct is { IsCancellationRequested: true })
{
return;
}
AutoFightContext.Instance.Simulator.LeftButtonClick();
ms -= 200;
Sleep(200, Ct);
}
}
///
/// 使用元素战技 E
///
public void UseSkill(bool hold = false)
{
for (var i = 0; i < 1; i++)
{
if (Ct is { IsCancellationRequested: true })
{
return;
}
if (hold)
{
if (Name == "纳西妲")
{
AutoFightContext.Instance.Simulator.KeyDown(User32.VK.VK_E);
Sleep(300, Ct);
for (int j = 0; j < 10; j++)
{
Simulation.SendInput.Mouse.MoveMouseBy(1000, 0);
Sleep(50); // 持续操作不应该被cts取消
}
Sleep(300); // 持续操作不应该被cts取消
AutoFightContext.Instance.Simulator.KeyUp(User32.VK.VK_E);
}
else if (Name == "坎蒂丝")
{
AutoFightContext.Instance.Simulator.KeyDown(User32.VK.VK_E);
Thread.Sleep(3000);
AutoFightContext.Instance.Simulator.KeyUp(User32.VK.VK_E);
}
else
{
AutoFightContext.Instance.Simulator.LongKeyPress(User32.VK.VK_E);
}
}
else
{
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_E);
}
Sleep(200, Ct);
var region = CaptureToRectArea();
ThrowWhenDefeated(region);
var cd = GetSkillCurrentCd(region);
if (cd > 0)
{
Logger.LogInformation(hold ? "{Name} 长按元素战技,cd:{Cd}" : "{Name} 点按元素战技,cd:{Cd}", Name, cd);
// todo 把cd加入执行队列
LastSkillTime = DateTime.UtcNow;
return;
}
}
}
///
/// 元素战技是否正在CD中
/// 右下 267x132
/// 77x77
///
public double GetSkillCurrentCd(ImageRegion imageRegion)
{
var eRa = imageRegion.DeriveCrop(AutoFightContext.Instance.FightAssets.ERect);
var text = OcrFactory.Paddle.Ocr(eRa.SrcGreyMat);
return StringUtils.TryParseDouble(text);
}
///
/// 使用元素爆发 Q
/// Q释放等待 2s 超时认为没有Q技能
///
public void UseBurst()
{
// var isBurstReleased = false;
for (var i = 0; i < 10; i++)
{
if (Ct is { IsCancellationRequested: true })
{
return;
}
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_Q);
Sleep(200, Ct);
var region = CaptureToRectArea();
ThrowWhenDefeated(region);
var notActiveCount = CombatScenes.Avatars.Count(avatar => !avatar.IsActive(region));
if (notActiveCount == 0)
{
// isBurstReleased = true;
Sleep(1500, Ct);
return;
}
// else
// {
// if (!isBurstReleased)
// {
// var cd = GetBurstCurrentCd(content);
// if (cd > 0)
// {
// Logger.LogInformation("{Name} 释放元素爆发,cd:{Cd}", Name, cd);
// // todo 把cd加入执行队列
// return;
// }
// }
// }
}
}
// ///
// /// 元素爆发是否正在CD中
// /// 右下 157x165
// /// 110x110
// ///
// public double GetBurstCurrentCd(CaptureContent content)
// {
// var qRa = content.CaptureRectArea.Crop(AutoFightContext.Instance.FightAssets.QRect);
// var text = OcrFactory.Paddle.Ocr(qRa.SrcGreyMat);
// return StringUtils.TryParseDouble(text);
// }
///
/// 冲刺
///
public void Dash(int ms = 0)
{
if (Ct is { IsCancellationRequested: true })
{
return;
}
if (ms == 0)
{
ms = 200;
}
AutoFightContext.Instance.Simulator.RightButtonDown();
Sleep(ms); // 冲刺不能被cts取消
AutoFightContext.Instance.Simulator.RightButtonUp();
}
public void Walk(string key, int ms)
{
if (Ct is { IsCancellationRequested: true })
{
return;
}
User32.VK vk = User32.VK.VK_NONAME;
if (key == "w")
{
vk = User32.VK.VK_W;
}
else if (key == "s")
{
vk = User32.VK.VK_S;
}
else if (key == "a")
{
vk = User32.VK.VK_A;
}
else if (key == "d")
{
vk = User32.VK.VK_D;
}
if (vk == User32.VK.VK_NONAME)
{
return;
}
AutoFightContext.Instance.Simulator.KeyDown(vk);
Sleep(ms); // 行走不能被cts取消
AutoFightContext.Instance.Simulator.KeyUp(vk);
}
///
/// 移动摄像机
///
/// 负数是左移,正数是右移
///
public void MoveCamera(int pixelDeltaX, int pixelDeltaY)
{
Simulation.SendInput.Mouse.MoveMouseBy(pixelDeltaX, pixelDeltaY);
}
///
/// 等待
///
///
public void Wait(int ms)
{
Sleep(ms); // 由于存在宏操作,等待不应被cts取消
}
///
/// 跳跃
///
public void Jump()
{
AutoFightContext.Instance.Simulator.KeyPress(User32.VK.VK_SPACE);
}
///
/// 重击
///
public void Charge(int ms = 0)
{
if (ms == 0)
{
ms = 1000;
}
if (Name == "那维莱特")
{
var dpi = TaskContext.Instance().DpiScale;
AutoFightContext.Instance.Simulator.LeftButtonDown();
while (ms >= 0)
{
if (Ct is { IsCancellationRequested: true })
{
return;
}
Simulation.SendInput.Mouse.MoveMouseBy((int)(1000 * dpi), 0);
ms -= 50;
Sleep(50); // 持续操作不应该被cts取消
}
AutoFightContext.Instance.Simulator.LeftButtonUp();
}
else
{
AutoFightContext.Instance.Simulator.LeftButtonDown();
Sleep(ms); // 持续操作不应该被cts取消
AutoFightContext.Instance.Simulator.LeftButtonUp();
}
}
public void MouseDown(string key = "left")
{
key = key.ToLower();
if (key == "left")
{
AutoFightContext.Instance.Simulator.LeftButtonDown();
}
else if (key == "right")
{
AutoFightContext.Instance.Simulator.RightButtonDown();
}
else if (key == "middle")
{
Simulation.SendInput.Mouse.MiddleButtonDown();
}
}
public void MouseUp(string key = "left")
{
key = key.ToLower();
if (key == "left")
{
AutoFightContext.Instance.Simulator.LeftButtonUp();
}
else if (key == "right")
{
AutoFightContext.Instance.Simulator.RightButtonUp();
}
else if (key == "middle")
{
Simulation.SendInput.Mouse.MiddleButtonUp();
}
}
public void Click(string key = "left")
{
key = key.ToLower();
if (key == "left")
{
AutoFightContext.Instance.Simulator.LeftButtonClick();
}
else if (key == "right")
{
AutoFightContext.Instance.Simulator.RightButtonClick();
}
else if (key == "middle")
{
Simulation.SendInput.Mouse.MiddleButtonClick();
}
}
public void MoveBy(int x, int y)
{
Simulation.SendInput.Mouse.MoveMouseBy(x, y);
}
public void KeyDown(string key)
{
var vk = User32Helper.ToVk(key);
AutoFightContext.Instance.Simulator.KeyDown(vk);
}
public void KeyUp(string key)
{
var vk = User32Helper.ToVk(key);
AutoFightContext.Instance.Simulator.KeyUp(vk);
}
public void KeyPress(string key)
{
var vk = User32Helper.ToVk(key);
AutoFightContext.Instance.Simulator.KeyPress(vk);
}
}