Files
better-genshin-impact/BetterGenshinImpact/GameTask/Common/BgiVision/BvStatus.cs
FishmanTheMurloc 6d4f8b80e8 Feat/multi lan (#1336)
* 记录一次对hutaofisher的访谈,帮助开发者理解其算法

* 本地化HelloWorld

* .csproj取消windows版本号,此处导致了IDE在新建代码文件和自动生成代码时,默认命名空间丢失的问题。已知VisualStudio和ReSharper存在这个问题。

* 优化扩展方法写法,改为从localizer扩展;Converter优化写法,避免冲突;新增两种语言,待测试ocr效果

* Revert ".csproj取消windows版本号,此处导致了IDE在新建代码文件和自动生成代码时,默认命名空间丢失的问题。已知VisualStudio和ReSharper存在这个问题。"

This reverts commit 8bd7ee74c5.

* localizer改为由构造函数传入以支持单元测试;一个英语上钩的单元测试

* 传送任务支持英语游戏界面;本地化参数挪至OtherConfig类下,但界面位置暂不挪动,待定

* 调整resx位置风格,放在直接使用字符串的类下;一条龙合成树脂及领取每日奖励支持游戏内中英双语

* 删除无用碎片文件

* 删去两个不必要的Sdcb包引用

* Paddle服务类去掉分类模型;检测和识别新增支持繁中和法语,配有单元测试;因小语种识别效果不理想,使用正则匹配替换多处识别文本相等或包含判断;钓鱼、一条龙合成树脂及领取每日奖励支持游戏内繁中和法语;

* 检查今日奖励任务的多语言化;右侧联机的P图标检测区域宽度缩减,避免英语角色名被误识别成P

* AutoDomainTask的游戏多语言化,由于我的游戏账号无法测试,仅配一些测试用例

* 修复有3个Mizuki导致异常的bug,临时用拼音代替新角色英文名,并为该数据初始化方法添加单元测试

* 瓦雷莎删去别名“牛牛”,因荒泷一斗已占用此别名;别名加载和读取优化

* 加个锁避免单元测试中多线程初始化paddle崩溃
2025-03-28 11:00:08 +08:00

241 lines
7.4 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.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.QuickTeleport.Assets;
using OpenCvSharp;
using System;
using System.Linq;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition;
using System.Threading;
using BetterGenshinImpact.GameTask.AutoFight.Assets;
using BetterGenshinImpact.GameTask.AutoSkip.Assets;
using BetterGenshinImpact.GameTask.GameLoading.Assets;
namespace BetterGenshinImpact.GameTask.Common.BgiVision;
/// <summary>
/// 模仿OpenCv的静态类
/// 用于原神的各类识别与控制操作
///
/// 此处主要是对游戏内的一些状态进行识别
/// </summary>
public static partial class Bv
{
public static string WhichGameUi()
{
throw new NotImplementedException();
}
/// <summary>
/// 是否在主界面
/// </summary>
/// <param name="captureRa"></param>
/// <returns></returns>
public static bool IsInMainUi(ImageRegion captureRa)
{
return captureRa.Find(ElementAssets.Instance.PaimonMenuRo).IsExist();
}
/// <summary>
/// 等待主界面加载完成
/// </summary>
/// <param name="ct"></param>
/// <param name="retryTimes"></param>
/// <returns></returns>
public static async Task<bool> WaitForMainUi(CancellationToken ct, int retryTimes = 10)
{
for (var i = 0; i < retryTimes; i++)
{
await TaskControl.Delay(1000, ct);
using var ra3 = TaskControl.CaptureToRectArea();
if (IsInMainUi(ra3))
{
return true;
}
}
return false;
}
/// <summary>
/// 在任意可以关闭的UI界面识别关闭按钮
/// </summary>
/// <param name="captureRa"></param>
/// <returns></returns>
public static bool IsInAnyClosableUi(ImageRegion captureRa)
{
return captureRa.Find(QuickTeleportAssets.Instance.MapCloseButtonRo).IsExist();
}
/// <summary>
/// 是否在队伍选择界面
/// </summary>
/// <param name="captureRa"></param>
/// <returns></returns>
public static bool IsInPartyViewUi(ImageRegion captureRa)
{
return captureRa.Find(ElementAssets.Instance.PartyBtnChooseView).IsExist();
}
/// <summary>
/// 等待队伍选择界面加载完成
/// </summary>
/// <param name="ct"></param>
/// <param name="retryTimes"></param>
/// <returns></returns>
public static async Task<bool> WaitForPartyViewUi(CancellationToken ct, int retryTimes = 5)
{
return await NewRetry.WaitForAction(() => IsInPartyViewUi(TaskControl.CaptureToRectArea()), ct, retryTimes);
}
/// <summary>
/// 是否在大地图界面
/// </summary>
/// <param name="captureRa"></param>
/// <returns></returns>
public static bool IsInBigMapUi(ImageRegion captureRa)
{
return captureRa.Find(QuickTeleportAssets.Instance.MapScaleButtonRo).IsExist();
}
/// <summary>
/// 大地图界面是否在地底
/// 鼠标悬浮在地下图标或者处于切换动画的时候可能会误识别
/// </summary>
/// <param name="captureRa"></param>
/// <returns></returns>
public static bool BigMapIsUnderground(ImageRegion captureRa)
{
return captureRa.Find(QuickTeleportAssets.Instance.MapUndergroundSwitchButtonRo).IsExist();
}
public static double GetBigMapScale(ImageRegion region)
{
var scaleRa = region.Find(QuickTeleportAssets.Instance.MapScaleButtonRo);
if (scaleRa.IsEmpty())
{
throw new Exception("当前未处于大地图界面不能使用GetBigMapScale方法");
}
// 原先这里的起止区间和config里写死的值差1
var start = TaskContext.Instance().Config.TpConfig.ZoomStartY;
var end = TaskContext.Instance().Config.TpConfig.ZoomEndY;
var cur = (scaleRa.Y + scaleRa.Height / 2.0) * TaskContext.Instance().SystemInfo.ZoomOutMax1080PRatio; // 转换到1080p坐标系,主要是小于1080p的情况
return (end * 1.0 - cur) / (end - start);
}
public static MotionStatus GetMotionStatus(ImageRegion captureRa)
{
var spaceExist = captureRa.Find(ElementAssets.Instance.SpaceKey).IsExist();
var xExist = captureRa.Find(ElementAssets.Instance.XKey).IsExist();
if (spaceExist)
{
return xExist ? MotionStatus.Climb : MotionStatus.Fly;
}
else
{
return MotionStatus.Normal;
}
}
/// <summary>
/// 是否出现复苏提示
/// </summary>
/// <param name="region"></param>
/// <returns></returns>
public static bool IsInRevivePrompt(ImageRegion region)
{
using var confirmRectArea = region.Find(AutoFightAssets.Instance.ConfirmRa);
if (!confirmRectArea.IsEmpty())
{
var list = region.FindMulti(new RecognitionObject
{
RecognitionType = RecognitionTypes.Ocr,
RegionOfInterest = new Rect(0, 0, region.Width, region.Height / 2)
});
if (list.Any(r => r.Text.Contains("复苏")))
{
return true;
}
}
return false;
}
/// <summary>
/// 是否出现全队死亡和复苏提示
/// </summary>
/// <param name="region"></param>
/// <returns></returns>
public static bool ClickIfInReviveModal(ImageRegion region)
{
var list = region.FindMulti(new RecognitionObject
{
RecognitionType = RecognitionTypes.Ocr,
RegionOfInterest = new Rect(0, region.Height / 4 * 3, region.Width, region.Height / 4)
});
var r = list.FirstOrDefault(r => r.Text.Contains("复苏"));
if (r != null)
{
r.Click();
return true;
}
return false;
}
/// <summary>
/// 当前角色是否低血量
/// </summary>
/// <param name="captureRa"></param>
/// <returns></returns>
public static bool CurrentAvatarIsLowHp(ImageRegion captureRa)
{
var assetScale = TaskContext.Instance().SystemInfo.AssetScale;
// 获取 (808, 1010) 位置的像素颜色
var pixelColor = captureRa.SrcMat.At<Vec3b>((int)(1010 * assetScale), (int)(808 * assetScale));
// 判断颜色是否是 (255, 90, 90)
return pixelColor is { Item2: 255, Item1: 90, Item0: 90 };
}
/// <summary>
/// 在空月祝福界面
/// </summary>
/// <param name="captureRa"></param>
/// <returns></returns>
public static bool IsInBlessingOfTheWelkinMoon(ImageRegion captureRa)
{
return captureRa.Find(GameLoadingAssets.Instance.WelkinMoonRo).IsExist();
}
/// <summary>
/// 是否在对话界面
/// </summary>
/// <param name="captureRa"></param>
/// <returns></returns>
public static bool IsInTalkUi(ImageRegion captureRa)
{
return captureRa.Find(AutoSkipAssets.Instance.DisabledUiButtonRo).IsExist();
}
/// <summary>
/// 等到对话界面加载完成
/// </summary>
/// <param name="ct"></param>
/// <param name="retryTimes"></param>
/// <returns></returns>
public static async Task<bool> WaitAndSkipForTalkUi(CancellationToken ct, int retryTimes = 5)
{
return await NewRetry.WaitForAction(() => IsInTalkUi(TaskControl.CaptureToRectArea()), ct, retryTimes, 500);
}
}
public enum MotionStatus
{
Normal, // 正常
Fly, // 飞行
Climb, // 攀爬
}