mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-25 10:05:49 +08:00
* 记录一次对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崩溃
241 lines
7.4 KiB
C#
241 lines
7.4 KiB
C#
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, // 攀爬
|
||
} |