拓展自动吃药,基于图标识别 (#2051)

This commit is contained in:
FishmanTheMurloc
2025-08-16 13:51:46 +08:00
committed by GitHub
parent 436a8d207e
commit 8a41148ba6
32 changed files with 674 additions and 105 deletions

View File

@@ -1,9 +1,10 @@
using CommunityToolkit.Mvvm.ComponentModel;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.AutoEat;
using BetterGenshinImpact.GameTask.AutoFight;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.AutoFight;
namespace BetterGenshinImpact.Core.Config;
@@ -96,6 +97,13 @@ public partial class PathingPartyConfig : ObservableObject
[ObservableProperty]
private bool _autoEatEnabled = false;
/// <summary>
/// 自动吃食物配置
/// 供JS脚本使用
/// </summary>
[ObservableProperty]
private AutoEatConfig _autoEatConfig = new();
//在连续执行时是否隐藏
[ObservableProperty]
private bool _hideOnRepeat = false;

View File

@@ -1,19 +1,26 @@
using BetterGenshinImpact.Core.Script.Dependence.Model;
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Script.Dependence.Model;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.ViewModel.Pages;
using System;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.AutoDomain;
using BetterGenshinImpact.GameTask.AutoEat;
using BetterGenshinImpact.GameTask.AutoFishing;
using BetterGenshinImpact.GameTask.AutoWood;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation;
using BetterGenshinImpact.GameTask.AutoPathing.Handler;
using BetterGenshinImpact.GameTask.AutoWood;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.ViewModel.Pages;
using Microsoft.ClearScript;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace BetterGenshinImpact.Core.Script.Dependence;
public class Dispatcher
{
private readonly ILogger<Dispatcher> _logger = App.GetLogger<Dispatcher>();
private object _config = null;
public Dispatcher(object config)
@@ -127,7 +134,7 @@ public class Dispatcher
// 如果没有自定义令牌,就使用全局令牌
cancellationToken = CancellationContext.Instance.Cts.Token;
}
// 根据名称执行任务
switch (soloTask.Name)
{
@@ -162,7 +169,69 @@ public class Dispatcher
await new AutoFishingTask(AutoFishingTaskParam.BuildFromSoloTaskConfig(soloTask.Config)).Start(
cancellationToken);
break;
case "AutoEat":
string? foodName = soloTask.Config == null ? null : ScriptObjectConverter.GetValue<string?>((ScriptObject)soloTask.Config, "foodName", null);
FoodEffectType? foodEffectType = soloTask.Config == null ? null : (FoodEffectType?)ScriptObjectConverter.GetValue<int?>((ScriptObject)soloTask.Config, "foodEffectType", null);
if (foodName != null && foodEffectType != null)
{
throw new NotSupportedException("不能同时指定foodName和foodEffectType");
}
if (foodName == null)
{
if (foodEffectType != null)
{
PathingPartyConfig? pathingPartyConfig = _config as PathingPartyConfig;
if (pathingPartyConfig == null)
{
throw new NotSupportedException("foodEffectType参数需要调度器配置请在调度器下使用");
}
else
{
switch (foodEffectType)
{
case FoodEffectType.ATKBoostingDish:
foodName = pathingPartyConfig.AutoEatConfig.DefaultAtkBoostingDishName;
if (foodName == null)
{
_logger.LogInformation("缺少{Text}配置跳过吃Buff", "默认的攻击类料理");
return;
}
break;
case FoodEffectType.AdventurersDish:
foodName = pathingPartyConfig.AutoEatConfig.DefaultAdventurersDishName;
if (foodName == null)
{
_logger.LogInformation("缺少{Text}配置跳过吃Buff", "默认的冒险类料理");
return;
}
break;
case FoodEffectType.DEFBoostingDish:
foodName = pathingPartyConfig.AutoEatConfig.DefaultDefBoostingDishName;
if (foodName == null)
{
_logger.LogInformation("缺少{Text}配置跳过吃Buff", "默认的防御类料理");
return;
}
break;
default:
throw new NotSupportedException("JS脚本入参错误错误的foodEffectType");
}
}
}
}
var autoEatConfig = TaskContext.Instance().Config.AutoEatConfig;
await new AutoEatTask(new AutoEatParam()
{
CheckInterval = autoEatConfig.CheckInterval,
EatInterval = autoEatConfig.EatInterval,
ShowNotification = autoEatConfig.ShowNotification,
FoodName = foodName
}).Start(cancellationToken);
break;
default:
throw new ArgumentException($"未知的任务名称: {soloTask.Name}", nameof(soloTask.Name));
}

View File

@@ -1,4 +1,4 @@
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using System;
using System.Diagnostics;
using System.Threading;
@@ -35,7 +35,6 @@ public class AutoArtifactSalvageTask : ISoloTask
{
private readonly ILogger logger = App.GetLogger<AutoArtifactSalvageTask>();
private readonly InputSimulator input = Simulation.SendInput;
private readonly ReturnMainUiTask _returnMainUiTask = new();
private CancellationToken ct;
@@ -51,7 +50,7 @@ public class AutoArtifactSalvageTask : ISoloTask
private readonly int? maxNumToCheck;
private bool returnToMainUi = true;
private readonly bool returnToMainUi = true;
public AutoArtifactSalvageTask(int star, string? regularExpression = null, int? maxNumToCheck = null)
{
@@ -75,12 +74,51 @@ public class AutoArtifactSalvageTask : ISoloTask
this.returnToMainUi = returnToMainUi;
}
public async Task Start(CancellationToken ct)
public static async Task OpenBag(GridScreenName gridScreenName, InputSimulator input, ILogger logger, CancellationToken ct)
{
this.ct = ct;
if (returnToMainUi)
RecognitionObject? recognitionObjectChecked;
RecognitionObject? recognitionObjectUnchecked;
switch (gridScreenName)
{
await _returnMainUiTask.Start(ct);
case GridScreenName.Weapons:
recognitionObjectChecked = ElementAssets.Instance.BagWeaponChecked;
recognitionObjectUnchecked = ElementAssets.Instance.BagWeaponUnchecked;
break;
case GridScreenName.Artifacts:
recognitionObjectChecked = ElementAssets.Instance.BagArtifactChecked;
recognitionObjectUnchecked = ElementAssets.Instance.BagArtifactUnchecked;
break;
case GridScreenName.CharacterDevelopmentItems:
recognitionObjectChecked = ElementAssets.Instance.BagCharacterDevelopmentItemChecked;
recognitionObjectUnchecked = ElementAssets.Instance.BagCharacterDevelopmentItemUnchecked;
break;
case GridScreenName.Food:
recognitionObjectChecked = ElementAssets.Instance.BagFoodChecked;
recognitionObjectUnchecked = ElementAssets.Instance.BagFoodUnchecked;
break;
case GridScreenName.Materials:
recognitionObjectChecked = ElementAssets.Instance.BagMaterialChecked;
recognitionObjectUnchecked = ElementAssets.Instance.BagMaterialUnchecked;
break;
case GridScreenName.Gadget:
recognitionObjectChecked = ElementAssets.Instance.BagGadgetChecked;
recognitionObjectUnchecked = ElementAssets.Instance.BagGadgetUnchecked;
break;
case GridScreenName.Quest:
recognitionObjectChecked = ElementAssets.Instance.BagQuestChecked;
recognitionObjectUnchecked = ElementAssets.Instance.BagQuestUnchecked;
break;
case GridScreenName.PreciousItems:
recognitionObjectChecked = ElementAssets.Instance.BagPreciousItemChecked;
recognitionObjectUnchecked = ElementAssets.Instance.BagPreciousItemUnchecked;
break;
case GridScreenName.Furnishings:
recognitionObjectChecked = ElementAssets.Instance.BagFurnishingChecked;
recognitionObjectUnchecked = ElementAssets.Instance.BagFurnishingUnchecked;
break;
default:
throw new NotSupportedException($"背包不支持的界面:{gridScreenName.GetDescription()}");
}
// B键打开背包
@@ -89,12 +127,11 @@ public class AutoArtifactSalvageTask : ISoloTask
var openBagSuccess = await NewRetry.WaitForAction(() =>
{
// 选择圣遗物
using var ra = CaptureToRectArea();
using var artifactBtn = ra.Find(ElementAssets.Instance.BagArtifactChecked);
using var artifactBtn = ra.Find(recognitionObjectChecked);
if (artifactBtn.IsEmpty())
{
using var artifactBtn2 = ra.Find(ElementAssets.Instance.BagArtifactUnchecked);
using var artifactBtn2 = ra.Find(recognitionObjectUnchecked);
if (artifactBtn2.IsExist())
{
artifactBtn2.Click();
@@ -118,12 +155,23 @@ public class AutoArtifactSalvageTask : ISoloTask
if (!openBagSuccess)
{
logger.LogError("未找到背包中圣遗物菜单按钮,打开背包失败");
logger.LogError("未找到背包中{name}菜单按钮,打开背包失败", gridScreenName.GetDescription());
return;
}
await Delay(800, ct);
}
public async Task Start(CancellationToken ct)
{
this.ct = ct;
if (returnToMainUi)
{
await new ReturnMainUiTask().Start(ct);
}
await OpenBag(GridScreenName.Artifacts, this.input, this.logger, this.ct);
// 点击分解按钮打开分解界面
using var ra2 = CaptureToRectArea();
@@ -217,7 +265,7 @@ public class AutoArtifactSalvageTask : ISoloTask
if (returnToMainUi)
{
await _returnMainUiTask.Start(ct);
await new ReturnMainUiTask().Start(ct);
}
}
}

View File

@@ -33,4 +33,28 @@ public partial class AutoEatConfig : ObservableObject
/// </summary>
[ObservableProperty]
private int _eatInterval = 2000;
/// <summary>
/// 测试食物名称
/// </summary>
[ObservableProperty]
private string? _testFoodName;
/// <summary>
/// 默认的攻击类料理名称
/// </summary>
[ObservableProperty]
private string? _defaultAtkBoostingDishName;
/// <summary>
/// 默认的冒险类料理名称
/// </summary>
[ObservableProperty]
private string? _defaultAdventurersDishName;
/// <summary>
/// 默认的防御类料理名称
/// </summary>
[ObservableProperty]
private string? _defaultDefBoostingDishName;
}

View File

@@ -22,16 +22,9 @@ public class AutoEatParam : BaseTaskParam
/// </summary>
public int EatInterval { get; set; }
public AutoEatParam()
{
SetDefault();
}
public void SetDefault()
{
var config = TaskContext.Instance().Config.AutoEatConfig;
ShowNotification = config.ShowNotification;
CheckInterval = config.CheckInterval;
EatInterval = config.EatInterval;
}
/// <summary>
/// 食物名称
/// 如果传空就使用便携营养袋,否则进入背包查找对应食物并使用
/// </summary>
public string? FoodName { get; set; }
}

View File

@@ -1,13 +1,19 @@
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoArtifactSalvage;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.GetGridIcons;
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.Service.Notification;
using BetterGenshinImpact.Service.Notification.Model.Enum;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.Model.GameUI;
using Fischless.WindowsInput;
using Microsoft.Extensions.Logging;
using Microsoft.ML.OnnxRuntime;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
@@ -24,6 +30,8 @@ public class AutoEatTask : BaseIndependentTask, ISoloTask
private readonly AutoEatParam _taskParam;
private readonly AutoEatConfig _config;
private readonly ILogger _logger = App.GetLogger<AutoEatTask>();
private readonly InputSimulator _input = Simulation.SendInput;
private CancellationToken _ct;
public AutoEatTask(AutoEatParam taskParam)
@@ -37,33 +45,75 @@ public class AutoEatTask : BaseIndependentTask, ISoloTask
_ct = ct;
Init();
Logger.LogInformation("自动吃药任务启动");
_logger.LogInformation("自动吃药任务启动");
if (!IsTakeFood())
if (String.IsNullOrWhiteSpace(_taskParam.FoodName))
{
Logger.LogWarning("未装备 \"{Tool}\",无法启用自动吃药功能", "便携营养袋");
return;
}
if (!IsTakeFood())
{
_logger.LogWarning("未装备 \"{Tool}\",无法启用自动吃药功能", "便携营养袋");
return;
}
try
{
await AutoEatLoop();
try
{
await AutoEatLoop();
}
catch (Exception e)
{
_logger.LogError(e, "自动吃药任务发生异常");
throw;
}
finally
{
_logger.LogInformation("自动吃药任务结束");
}
}
catch (Exception e)
else
{
Logger.LogError(e, "自动吃药任务发生异常");
throw;
}
finally
{
Logger.LogInformation("自动吃药任务结束");
_logger.LogInformation("打开背包寻找{name}……", _taskParam.FoodName);
await new ReturnMainUiTask().Start(ct);
await AutoArtifactSalvageTask.OpenBag(GridScreenName.Food, _input, _logger, _ct);
using InferenceSession session = GridIconsAccuracyTestTask.LoadModel(out Dictionary<string, float[]> prototypes);
using var ra0 = CaptureToRectArea();
GridScreenParams gridParams = GridScreenParams.Templates[GridScreenName.Food];
var gridRoi = gridParams.GetRect(ra0);
GridScreen gridScreen = new GridScreen(gridRoi, gridParams, _logger, _ct);
bool isAte = false;
await foreach (ImageRegion itemRegion in gridScreen)
{
var result = GridIconsAccuracyTestTask.Infer(itemRegion.SrcMat, session, prototypes);
string predName = result.Item1;
if (predName == _taskParam.FoodName)
{
// 点击item
itemRegion.Click();
await Delay(300, ct);
// 点击确定
using var ra = ra0.Find(ElementAssets.Instance.BtnWhiteConfirm);
if (ra.IsExist())
{
ra.Click();
}
_logger.LogInformation("吃了一份{name},真香!", predName);
isAte = true;
break;
}
}
if (!isAte)
{
_logger.LogInformation("没有找到{name}", _taskParam.FoodName);
}
await new ReturnMainUiTask().Start(ct);
}
}
private void Init()
{
Logger.LogInformation("→ {Text} 检测间隔: {Interval}ms", "自动吃药,", _config.CheckInterval);
Logger.LogInformation("→ {Text} 吃药间隔: {Interval}ms", "自动吃药,", _config.EatInterval);
_logger.LogInformation("→ {Text} 检测间隔: {Interval}ms", "自动吃药,", _config.CheckInterval);
_logger.LogInformation("→ {Text} 吃药间隔: {Interval}ms", "自动吃药,", _config.EatInterval);
}
/// <summary>
@@ -87,8 +137,8 @@ public class AutoEatTask : BaseIndependentTask, ISoloTask
// 模拟按键 "Z" 使用便携营养袋
Simulation.SendInput.SimulateAction(GIActions.QuickUseGadget);
lastEatTime = now;
Logger.LogInformation("检测到红血,自动吃药");
_logger.LogInformation("检测到红血,自动吃药");
}
}
@@ -101,7 +151,7 @@ public class AutoEatTask : BaseIndependentTask, ISoloTask
}
catch (Exception e)
{
Logger.LogDebug(e, "自动吃药检测时发生异常");
_logger.LogDebug(e, "自动吃药检测时发生异常");
await Delay(1000, _ct); // 异常时稍作等待
}
}
@@ -124,7 +174,7 @@ public class AutoEatTask : BaseIndependentTask, ISoloTask
}
catch (Exception e)
{
Logger.LogDebug(e, "检测便携营养袋时发生异常");
_logger.LogDebug(e, "检测便携营养袋时发生异常");
return false;
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace BetterGenshinImpact.GameTask.AutoEat
{
public enum FoodEffectType
{
[Description("恢复类料理")]
RecoveryDish,
[Description("攻击类料理")]
ATKBoostingDish,
[Description("冒险类料理")]
AdventurersDish,
[Description("防御类料理")]
DEFBoostingDish,
[Description("药剂")]
Potion,
[Description("其他")]
Other
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,4 +1,4 @@
using System;
using System;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.Helpers.Extensions;
@@ -35,8 +35,24 @@ public class ElementAssets : BaseAssets<ElementAssets>
public RecognitionObject Keyreduce;
public RecognitionObject Keyincrease;
public RecognitionObject BagWeaponUnchecked;
public RecognitionObject BagWeaponChecked;
public RecognitionObject BagArtifactUnchecked;
public RecognitionObject BagArtifactChecked;
public RecognitionObject BagCharacterDevelopmentItemUnchecked;
public RecognitionObject BagCharacterDevelopmentItemChecked;
public RecognitionObject BagFoodUnchecked;
public RecognitionObject BagFoodChecked;
public RecognitionObject BagMaterialUnchecked;
public RecognitionObject BagMaterialChecked;
public RecognitionObject BagGadgetUnchecked;
public RecognitionObject BagGadgetChecked;
public RecognitionObject BagQuestUnchecked;
public RecognitionObject BagQuestChecked;
public RecognitionObject BagPreciousItemUnchecked;
public RecognitionObject BagPreciousItemChecked;
public RecognitionObject BagFurnishingUnchecked;
public RecognitionObject BagFurnishingChecked;
public RecognitionObject BtnArtifactSalvage;
public RecognitionObject BtnArtifactSalvageConfirm;
@@ -245,7 +261,26 @@ public class ElementAssets : BaseAssets<ElementAssets>
DrawOnWindow = false
}.InitTemplate();
// 分解圣遗物
// 背包武器
BagWeaponUnchecked = new RecognitionObject
{
Name = "BagWeaponUnchecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_weapon_unchecked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.87,
DrawOnWindow = false
}.InitTemplate();
BagWeaponChecked = new RecognitionObject
{
Name = "BagWeaponChecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_weapon_checked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.8,
DrawOnWindow = false
}.InitTemplate();
// 背包圣遗物
BagArtifactUnchecked = new RecognitionObject
{
Name = "BagArtifactUnchecked",
@@ -264,6 +299,141 @@ public class ElementAssets : BaseAssets<ElementAssets>
Threshold = 0.8,
DrawOnWindow = false
}.InitTemplate();
// 背包养成道具
BagCharacterDevelopmentItemUnchecked = new RecognitionObject
{
Name = "BagCharacterDevelopmentItemUnchecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_characterdevelopmentitem_unchecked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.87,
DrawOnWindow = false
}.InitTemplate();
BagCharacterDevelopmentItemChecked = new RecognitionObject
{
Name = "BagCharacterDevelopmentItemChecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_characterdevelopmentitem_checked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.8,
DrawOnWindow = false
}.InitTemplate();
// 背包食物
BagFoodUnchecked = new RecognitionObject
{
Name = "BagFoodUnchecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_food_unchecked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.87,
DrawOnWindow = false
}.InitTemplate();
BagFoodChecked = new RecognitionObject
{
Name = "BagFoodChecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_food_checked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.8,
DrawOnWindow = false
}.InitTemplate();
// 背包材料
BagMaterialUnchecked = new RecognitionObject
{
Name = "BagMaterialUnchecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_material_unchecked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.87,
DrawOnWindow = false
}.InitTemplate();
BagMaterialChecked = new RecognitionObject
{
Name = "BagMaterialChecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_material_checked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.8,
DrawOnWindow = false
}.InitTemplate();
// 背包小道具
BagGadgetUnchecked = new RecognitionObject
{
Name = "BagGadgetUnchecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_gadget_unchecked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.87,
DrawOnWindow = false
}.InitTemplate();
BagGadgetChecked = new RecognitionObject
{
Name = "BagGadgetChecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_gadget_checked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.8,
DrawOnWindow = false
}.InitTemplate();
// 背包任务
BagQuestUnchecked = new RecognitionObject
{
Name = "BagQuestUnchecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_quest_unchecked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.87,
DrawOnWindow = false
}.InitTemplate();
BagQuestChecked = new RecognitionObject
{
Name = "BagQuestChecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_quest_checked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.8,
DrawOnWindow = false
}.InitTemplate();
// 背包贵重道具
BagPreciousItemUnchecked = new RecognitionObject
{
Name = "BagPreciousItemUnchecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_preciousitem_unchecked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.87,
DrawOnWindow = false
}.InitTemplate();
BagPreciousItemChecked = new RecognitionObject
{
Name = "BagPreciousItemChecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_preciousitem_checked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.8,
DrawOnWindow = false
}.InitTemplate();
// 背包摆设
BagFurnishingUnchecked = new RecognitionObject
{
Name = "BagFurnishingUnchecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_furnishing_unchecked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.87,
DrawOnWindow = false
}.InitTemplate();
BagFurnishingChecked = new RecognitionObject
{
Name = "BagFurnishingChecked",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(@"Common\Element", "bag_furnishing_checked.png"),
RegionOfInterest = CaptureRect.CutTop(0.1),
Threshold = 0.8,
DrawOnWindow = false
}.InitTemplate();
// 分解圣遗物
BtnArtifactSalvage = new RecognitionObject
{
Name = "BtnArtifactSalvage",

View File

@@ -1,4 +1,4 @@
using BetterGenshinImpact.GameTask.Model.GameUI;
using BetterGenshinImpact.GameTask.Model.GameUI;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
@@ -8,8 +8,7 @@ namespace BetterGenshinImpact.GameTask.GetGridIcons;
public partial class GetGridIconsConfig : ObservableObject
{
/// <summary>
/// 昼夜策略
/// 钓全天的鱼、还是只钓白天或夜晚的鱼
/// Grid界面名称
/// </summary>
[ObservableProperty]
private GridScreenName _gridName = GridScreenName.Weapons;

View File

@@ -1,17 +1,21 @@
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.AutoArtifactSalvage;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.Model.GameUI;
using BetterGenshinImpact.Helpers.Extensions;
using Fischless.WindowsInput;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using Microsoft.Extensions.Localization;
using BetterGenshinImpact.GameTask.Model.Area;
using System.Collections.Generic;
using OpenCvSharp;
using System.Linq;
using BetterGenshinImpact.Core.Recognition.OCR;
using System.IO;
using OpenCvSharp.Extensions;
using BetterGenshinImpact.GameTask.Model.GameUI;
namespace BetterGenshinImpact.GameTask.GetGridIcons;
@@ -21,6 +25,7 @@ namespace BetterGenshinImpact.GameTask.GetGridIcons;
public class GetGridIconsTask : ISoloTask
{
private readonly ILogger logger = App.GetLogger<GetGridIconsTask>();
private readonly InputSimulator input = Simulation.SendInput;
private CancellationToken ct;
@@ -37,13 +42,31 @@ public class GetGridIconsTask : ISoloTask
this.gridScreenName = gridScreenName;
this.starAsSuffix = starAsSuffix;
this.maxNumToGet = maxNumToGet;
IStringLocalizer<GetGridIconsTask> stringLocalizer = App.GetService<IStringLocalizer<GetGridIconsTask>>() ?? throw new NullReferenceException();
}
public async Task Start(CancellationToken ct)
{
this.ct = ct;
switch (this.gridScreenName)
{
case GridScreenName.Weapons:
case GridScreenName.Artifacts:
case GridScreenName.CharacterDevelopmentItems:
case GridScreenName.Food:
case GridScreenName.Materials:
case GridScreenName.Gadget:
case GridScreenName.Quest:
case GridScreenName.PreciousItems:
case GridScreenName.Furnishings:
await new ReturnMainUiTask().Start(ct);
await AutoArtifactSalvageTask.OpenBag(this.gridScreenName, this.input, this.logger, this.ct);
break;
default:
logger.LogInformation("{name}暂不支持自动打开,请提前手动打开界面", gridScreenName.GetDescription());
break;
}
using var ra0 = CaptureToRectArea();
GridScreenParams gridParams = GridScreenParams.Templates[this.gridScreenName];
Rect gridRoi = gridParams.GetRect(ra0);

View File

@@ -1,6 +1,11 @@
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.AutoArtifactSalvage;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.Model.GameUI;
using BetterGenshinImpact.Helpers.Extensions;
using Fischless.WindowsInput;
using Microsoft.Extensions.Logging;
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
@@ -21,6 +26,7 @@ namespace BetterGenshinImpact.GameTask.GetGridIcons;
public class GridIconsAccuracyTestTask : ISoloTask
{
private readonly ILogger logger = App.GetLogger<GetGridIconsTask>();
private readonly InputSimulator input = Simulation.SendInput;
private CancellationToken ct;
@@ -36,25 +42,28 @@ public class GridIconsAccuracyTestTask : ISoloTask
this.maxNumToTest = maxNumToTest;
}
public async Task Start(CancellationToken ct)
/// <summary>
/// 加载图标识别模型
/// </summary>
/// <param name="prototypes">原型向量</param>
/// <returns>推理会话</returns>
/// <exception cref="Exception"></exception>
public static InferenceSession LoadModel(out Dictionary<string, float[]> prototypes)
{
this.ct = ct;
#region model
using var session = new InferenceSession(@".\GameTask\GetGridIcons\gridIcon.onnx"); // todo 所有数据炼好后放到onnx统一存放的位置去
var session = new InferenceSession(@".\GameTask\GetGridIcons\gridIcon.onnx"); // todo 所有数据炼好后放到onnx统一存放的位置去
var metadata = session.ModelMetadata;
if (!metadata.CustomMetadataMap.TryGetValue("prefix_list", out string? prefixListJson))
{
logger.LogError("模型文件缺少prefix_list");
return;
throw new Exception("模型文件缺少prefix_list");
}
List<string> prefixList = System.Text.Json.JsonSerializer.Deserialize<List<string>>(prefixListJson) ?? throw new Exception(); // 不预测前缀
#endregion
#region
var allLines = File.ReadLines(@".\GameTask\GetGridIcons\训练集原型特征.csv").Skip(1); // 跳过首行列名
Dictionary<string, float[]> prototypes = new Dictionary<string, float[]>();
prototypes = new Dictionary<string, float[]>();
foreach (string line in allLines)
{
var columns = line.Split(",").ToArray();
@@ -65,6 +74,33 @@ public class GridIconsAccuracyTestTask : ISoloTask
prototypes.Add(columns[0], flatData);
}
#endregion
return session;
}
public async Task Start(CancellationToken ct)
{
this.ct = ct;
switch (this.gridScreenName)
{
case GridScreenName.Weapons:
case GridScreenName.Artifacts:
case GridScreenName.CharacterDevelopmentItems:
case GridScreenName.Food:
case GridScreenName.Materials:
case GridScreenName.Gadget:
case GridScreenName.Quest:
case GridScreenName.PreciousItems:
case GridScreenName.Furnishings:
await new ReturnMainUiTask().Start(ct);
await AutoArtifactSalvageTask.OpenBag(this.gridScreenName, this.input, this.logger, this.ct);
break;
default:
logger.LogInformation("{name}暂不支持自动打开,请提前手动打开界面", gridScreenName.GetDescription());
break;
}
using InferenceSession session = LoadModel(out Dictionary<string, float[]> prototypes);
using var ra0 = CaptureToRectArea();
GridScreenParams gridParams = GridScreenParams.Templates[this.gridScreenName];
@@ -89,6 +125,8 @@ public class GridIconsAccuracyTestTask : ISoloTask
await Task.WhenAll(task1, task2);
(string, int) result = task2.Result;
string predName = result.Item1;
int predStarNum = result.Item2;
// 用CV方法得到的结果
using var ra1 = CaptureToRectArea();
@@ -101,14 +139,14 @@ public class GridIconsAccuracyTestTask : ISoloTask
// 统计结果
total_count++;
if (itemName.Contains(result.Item1) && result.Item2 == itemStarNum)
if (itemName.Contains(predName) && predStarNum == itemStarNum)
{
total_acc++;
logger.LogInformation($"{result.Item1}|{result.Item2}星,✔,正确率{total_acc / total_count:0.00}");
logger.LogInformation($"{predName}|{predStarNum}星,✔,正确率{total_acc / total_count:0.00}");
}
else
{
logger.LogInformation($"{result.Item1}|{result.Item2}星,应为:{itemName}|{itemStarNum}星,❌,正确率{total_acc / total_count:0.00}");
logger.LogInformation($"{predName}|{predStarNum}星,应为:{itemName}|{itemStarNum}星,❌,正确率{total_acc / total_count:0.00}");
}
count--;

View File

@@ -1,4 +1,4 @@
using Microsoft.ClearScript;
using Microsoft.ClearScript;
using System;
using System.Reflection;
@@ -45,7 +45,7 @@ public class ScriptObjectConverter
if (source[propertyName] is not Undefined && source[propertyName] != null)
{
object value = source.GetProperty(propertyName);
return (T)Convert.ChangeType(value, typeof(T));
return (T)value;
}
return defaultValue;
}

View File

@@ -782,7 +782,8 @@
ItemsSource=' 1234'
SelectedIndex="{Binding Config.AutoFightConfig.GuardianAvatar, Mode=TwoWay}"
SelectedItem="{Binding Config.AutoFightConfig.GuardianAvatar, Mode=TwoWay}" />
<!--todo ItemsSource=' 1234'导致XAML设计器无法加载-->
<ui:TextBlock Grid.Row="0"
Grid.Column="2"
Margin="0,0,8,0"
@@ -2395,7 +2396,7 @@
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0"
Visibility="{Binding Config.CommonConfig.ScreenshotEnabled, Converter={StaticResource BooleanToVisibilityConverter}}">
<ui:CardExpander.Icon>
<ui:FontIcon Glyph="&#xf4bb;" Style="{StaticResource FaFontIconStyle}" />
<ui:FontIcon Glyph="&#x1F527;" Style="{StaticResource FaFontIconStyle}" />
</ui:CardExpander.Icon>
<ui:CardExpander.Header>
<Grid>
@@ -2580,7 +2581,7 @@
</Grid>
</StackPanel>
</ui:CardExpander>
<!--<ui:CardExpander Margin="0,0,0,12" ContentPadding="0" Icon="{ui:SymbolIcon Accessibility24}">
<ui:CardExpander.Header>
<Grid>

View File

@@ -1,4 +1,4 @@
<UserControl x:Class="BetterGenshinImpact.View.Pages.View.ScriptGroupConfigView"
<UserControl x:Class="BetterGenshinImpact.View.Pages.View.ScriptGroupConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
@@ -8,20 +8,20 @@
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Width="800"
d:DataContext="{d:DesignInstance Type=pages:ScriptGroupConfigViewModel}"
d:DesignHeight="850"
d:DesignHeight="1600"
d:DesignWidth="800"
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
mc:Ignorable="d">
<ScrollViewer Height="800"
d:DesignHeight="1600"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<StackPanel Width="700" Margin="42,16,42,12">
<StackPanel Height="Auto" Width="700" Margin="42,16,42,12">
<ui:CardExpander Margin="0,0,0,12"
ContentPadding="0"
Icon="{ui:SymbolIcon Map24}"
IsExpanded="True">
Icon="{ui:SymbolIcon Map24}">
<ui:CardExpander.Header>
<Grid>
<Grid.RowDefinitions>
@@ -743,8 +743,7 @@
</StackPanel>
</ui:CardExpander>
<ui:CardExpander Margin="0,0,0,12"
ContentPadding="0"
IsExpanded="True">
ContentPadding="0">
<ui:CardExpander.Icon>
<ui:FontIcon Glyph="&#xf71d;" Style="{StaticResource FaFontIconStyle}" />
</ui:CardExpander.Icon>
@@ -1130,8 +1129,9 @@
ItemsSource=' 1234'
SelectedIndex="{Binding PathingConfig.AutoFightConfig.GuardianAvatar, Mode=TwoWay}"
SelectedItem="{Binding PathingConfig.AutoFightConfig.GuardianAvatar, Mode=TwoWay}" />
<ui:TextBlock Grid.Row="0"
<!--todo ItemsSource=' 1234'导致XAML设计器无法加载-->
<ui:TextBlock Grid.Row="0"
Grid.Column="2"
Margin="0,0,8,0"
HorizontalAlignment="Right"
@@ -1427,10 +1427,127 @@
</Grid>
</StackPanel>
</ui:CardExpander>
<!-- 自动吃食物 -->
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0">
<ui:CardExpander.Icon>
<ui:FontIcon Glyph="&#x1F35C;" Style="{StaticResource FaFontIconStyle}" />
</ui:CardExpander.Icon>
<ui:CardExpander.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="自动吃食物配置"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
供JS脚本调用Buff类食物配置食物名称会忽略“美味的”等前缀请填不带前缀的名称
</ui:TextBlock>
</Grid>
</ui:CardExpander.Header>
<StackPanel>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="默认的攻击类料理名称"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
入参"foodEffectType":1时使用
</ui:TextBlock>
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding PathingConfig.AutoEatConfig.DefaultAtkBoostingDishName, Mode=TwoWay}"
TextWrapping="Wrap" Cursor="IBeam" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="默认的冒险类料理名称"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
入参"foodEffectType":2时使用
</ui:TextBlock>
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding PathingConfig.AutoEatConfig.DefaultAdventurersDishName, Mode=TwoWay}"
TextWrapping="Wrap" Cursor="IBeam" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="默认的防御类料理名称"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
入参"foodEffectType":3时使用
</ui:TextBlock>
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding PathingConfig.AutoEatConfig.DefaultDefBoostingDishName, Mode=TwoWay}"
TextWrapping="Wrap" Cursor="IBeam" />
</Grid>
</StackPanel>
</ui:CardExpander>
<ui:CardExpander Margin="0,0,0,12"
ContentPadding="0"
Icon="{ui:SymbolIcon FlashSettings24}"
IsExpanded="True">
Icon="{ui:SymbolIcon FlashSettings24}">
<ui:CardExpander.Header>
<Grid>
<Grid.RowDefinitions>

View File

@@ -123,7 +123,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
private List<string> _domainNameList;
public static List<string> ArtifactSalvageStarList = ["4", "3", "2", "1"];
public static List<int> BossNumList = [1, 2, 3];
@@ -225,6 +225,12 @@ public partial class TaskSettingsPageViewModel : ViewModel
SwitchAutoDomainEnabled = false;
SwitchAutoFightEnabled = false;
SwitchAutoMusicGameEnabled = false;
SwitchAutoAlbumEnabled = false;
SwitchAutoFishingEnabled = false;
SwitchArtifactSalvageEnabled = false;
SwitchAutoRedeemCodeEnabled = false;
SwitchAutoStygianOnslaughtEnabled = false;
SwitchGetGridIconsEnabled = false;
await Task.Delay(800);
}
@@ -597,20 +603,20 @@ public partial class TaskSettingsPageViewModel : ViewModel
p.Height = 500;
p.ShowDialog();
if (p.DialogResult == true && !string.IsNullOrWhiteSpace(multilineTextBox.Text))
{
{
char[] separators = ['\r', '\n'];
var codes = multilineTextBox.Text.Split(separators, StringSplitOptions.RemoveEmptyEntries)
var codes = multilineTextBox.Text.Split(separators, StringSplitOptions.RemoveEmptyEntries)
.Select(code => code.Trim())
.Where(code => !string.IsNullOrEmpty(code))
.ToList();
.Select(code => code.Trim())
.Where(code => !string.IsNullOrEmpty(code))
.ToList();
if (codes.Count == 0)
{
Toast.Warning("没有有效的兑换码");
return;
}
SwitchAutoRedeemCodeEnabled = true;
await new TaskRunner()
.RunSoloTaskAsync(new UseRedemptionCodeTask(codes));