Files
better-genshin-impact/BetterGenshinImpact/GameTask/GameTaskManager.cs
FishmanTheMurloc 57d33c4312 又一波钓鱼优化 (#1301)
* GetFishBarRect方法添加更复杂的算法,并为其配备独立的单元测试,和分离难度较大的测试用例(未熟练时两侧出现黄色动态折线的情况);GetFishBoxArea行为去掉拉条框初始位置必须位于屏幕中轴线的条件,并添加其后续Fishing行为的单元测试来验证可行性;EnterFishingMode行为使用结束时间来代替Sleep,并添加整体超时时间;添加一个鱼咬钩的假阳性测试用例仅供娱乐

* 补充GetFishBarRect算法,使通过遗漏的测试"20250314002439020_Fishing_Succeeded.png"

* 拉条增加1秒未检测持续时间以应对瞬间丢失拉条框的情况;新增一个检查提竿结果的行为;新增一个检查开始钓一条鱼的初始状态的方法,以应对行为状态错配的情况;一些行为将Sleep优化为DateTime;修改上述改动对应的单元测试

* 解决合并冲突剩余问题,删掉ImageRegion的Bitmap构造函数重载

* 提供给测试用例初始化的 SystemInfo、TaskContext 方法,使用 InitForTest 即可

* InitForTest

* 和鸭蛋昨夜的提交撞车了。。。抽象了ISystemInto供单元测试实例化Fake类;给BaseAssets类定义了成员字段systemInfo(我想,既然都是图片模板数据集,如此定义是合理的),供继承类AutoFishingAssets使用,并定义了其在单元测试的派生类;添加了一个900p的选取鱼饵测试用例;blackboard改为负责携带AutoFishingAssets,并将其实例化时机挪到独立任务的Start方法中,避免由于TaskContext尚未初始化导致获取到的SystemInfo为空

* 一个特殊的测试用例:抛竿的瞬间、开始检测咬杆时遇到了假阳性

* Revert "InitForTest"

This reverts commit 225e9783a7.

* Revert "提供给测试用例初始化的 SystemInfo、TaskContext 方法,使用 InitForTest 即可"

This reverts commit 610c57263a.

* 为始终没有找到落点的情况添加计数,在第3次时直接退出,并添加此情况的单元测试

---------

Co-authored-by: 辉鸭蛋 <huiyadanli@gmail.com>
2025-03-18 19:51:42 +08:00

179 lines
6.6 KiB
C#

using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Script.Dependence.Model.TimerConfig;
using BetterGenshinImpact.GameTask.AutoFight.Assets;
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Assets;
using BetterGenshinImpact.GameTask.AutoPick.Assets;
using BetterGenshinImpact.GameTask.AutoSkip.Assets;
using BetterGenshinImpact.GameTask.AutoWood.Assets;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.GameLoading;
using BetterGenshinImpact.GameTask.GameLoading.Assets;
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.GameTask.Placeholder;
using BetterGenshinImpact.GameTask.QuickSereniteaPot.Assets;
using BetterGenshinImpact.GameTask.QuickTeleport.Assets;
using BetterGenshinImpact.View.Drawable;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using OpenCvSharp;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace BetterGenshinImpact.GameTask;
internal class GameTaskManager
{
public static ConcurrentDictionary<string, ITaskTrigger>? TriggerDictionary { get; set; }
/// <summary>
/// 一定要在任务上下文初始化完毕后使用
/// </summary>
/// <returns></returns>
public static List<ITaskTrigger> LoadInitialTriggers()
{
ReloadAssets();
TriggerDictionary = new ConcurrentDictionary<string, ITaskTrigger>();
TriggerDictionary.TryAdd("RecognitionTest", new TestTrigger());
TriggerDictionary.TryAdd("GameLoading", new GameLoadingTrigger());
TriggerDictionary.TryAdd("AutoPick", new AutoPick.AutoPickTrigger());
TriggerDictionary.TryAdd("QuickTeleport", new QuickTeleport.QuickTeleportTrigger());
TriggerDictionary.TryAdd("AutoSkip", new AutoSkip.AutoSkipTrigger());
TriggerDictionary.TryAdd("AutoFish", new AutoFishing.AutoFishingTrigger());
TriggerDictionary.TryAdd("AutoCook", new AutoCook.AutoCookTrigger());
return ConvertToTriggerList();
}
public static List<ITaskTrigger> ConvertToTriggerList(bool allEnabled = false)
{
if (TriggerDictionary is null)
{
return [];
}
var loadedTriggers = TriggerDictionary.Values.ToList();
loadedTriggers.ForEach(i => i.Init());
if (allEnabled)
{
loadedTriggers.ForEach(i => i.IsEnabled = true);
}
loadedTriggers = [.. loadedTriggers.OrderByDescending(i => i.Priority)];
return loadedTriggers;
}
public static void ClearTriggers()
{
TriggerDictionary?.Clear();
}
/// <summary>
/// 通过名称添加触发器
/// </summary>
/// <param name="name"></param>
/// <param name="externalConfig"></param>
public static void AddTrigger(string name, object? externalConfig)
{
TriggerDictionary ??= new ConcurrentDictionary<string, ITaskTrigger>();
TriggerDictionary.Clear(); //TODO 有问题,不应该清理
if (name == "AutoPick")
{
TriggerDictionary.TryAdd("AutoPick", new AutoPick.AutoPickTrigger(externalConfig as AutoPickExternalConfig));
}
else if (name == "AutoSkip")
{
TriggerDictionary.TryAdd("AutoSkip", new AutoSkip.AutoSkipTrigger());
}
// else if (name == "AutoFish")
// {
// TriggerDictionary.Add("AutoFish", new AutoFishing.AutoFishingTrigger());
// }
}
public static void RefreshTriggerConfigs()
{
if (TriggerDictionary is { Count: > 0 })
{
TriggerDictionary.GetValueOrDefault("AutoPick")?.Init();
TriggerDictionary.GetValueOrDefault("AutoSkip")?.Init();
TriggerDictionary.GetValueOrDefault("AutoFish")?.Init();
TriggerDictionary.GetValueOrDefault("QuickTeleport")?.Init();
// TriggerDictionary.GetValueOrDefault("GameLoading")?.Init();
TriggerDictionary.GetValueOrDefault("AutoCook")?.Init();
// 清理画布
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<object>(new object(), "RemoveAllButton", new object(), ""));
VisionContext.Instance().DrawContent.ClearAll();
}
ReloadAssets();
}
public static void ReloadAssets()
{
AutoPickAssets.DestroyInstance();
AutoSkipAssets.DestroyInstance();
AutoFishingAssets.DestroyInstance();
QuickTeleportAssets.DestroyInstance();
AutoWoodAssets.DestroyInstance();
AutoGeniusInvokationAssets.DestroyInstance();
AutoFightAssets.DestroyInstance();
ElementAssets.DestroyInstance();
QuickSereniteaPotAssets.DestroyInstance();
GameLoadingAssets.DestroyInstance();
MapLazyAssets.DestroyInstance();
}
/// <summary>
/// 获取素材图片并缩放
/// todo 支持多语言
/// </summary>
/// <param name="featName">任务名称</param>
/// <param name="assertName">素材文件名</param>
/// <param name="flags"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public static Mat LoadAssetImage(string featName, string assertName, ImreadModes flags = ImreadModes.Color)
{
return LoadAssetImage(featName, assertName, TaskContext.Instance().SystemInfo, flags);
}
/// <summary>
/// 获取素材图片并缩放
/// </summary>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public static Mat LoadAssetImage(string featName, string assertName, ISystemInfo systemInfo, ImreadModes flags = ImreadModes.Color)
{
var assetsFolder = Global.Absolute($@"GameTask\{featName}\Assets\{systemInfo.GameScreenSize.Width}x{systemInfo.GameScreenSize.Height}");
if (!Directory.Exists(assetsFolder))
{
assetsFolder = Global.Absolute($@"GameTask\{featName}\Assets\1920x1080");
}
if (!Directory.Exists(assetsFolder))
{
throw new FileNotFoundException($"未找到{featName}的素材文件夹");
}
var filePath = Path.Combine(assetsFolder, assertName);
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"未找到{featName}中的{assertName}文件");
}
var mat = Mat.FromStream(File.OpenRead(filePath), flags);
if (systemInfo.GameScreenSize.Width != 1920)
{
mat = ResizeHelper.Resize(mat, systemInfo.AssetScale);
}
return mat;
}
}