圣遗物分解筛选改用JS (#2087)

This commit is contained in:
FishmanTheMurloc
2025-08-22 10:31:14 +08:00
committed by GitHub
parent d423b6d7cb
commit 2753a81c1f
8 changed files with 437 additions and 57 deletions

View File

@@ -0,0 +1,85 @@
using System.Collections.Frozen;
using System.Collections.Generic;
namespace BetterGenshinImpact.GameTask.AutoArtifactSalvage
{
/// <summary>
/// 圣遗物词条
/// </summary>
public class ArtifactAffix
{
public ArtifactAffix(ArtifactAffixType type, float value)
{
Type = type;
Value = value;
}
public ArtifactAffixType Type { get; private set; }
public float Value { get; private set; }
public static FrozenDictionary<ArtifactAffixType, string> DefaultStrDic { get; } = new Dictionary<ArtifactAffixType, string>() {
{ ArtifactAffixType.ATK, "攻击力" },
{ ArtifactAffixType.ATKPercent, "攻击力" },
{ ArtifactAffixType.DEF, "防御力" },
{ ArtifactAffixType.DEFPercent, "防御力" },
{ ArtifactAffixType.HP, "生命值" },
{ ArtifactAffixType.HPPercent, "生命值" },
{ ArtifactAffixType.CRITRate, "暴击率" },
{ ArtifactAffixType.CRITDMG, "暴击伤害" },
{ ArtifactAffixType.ElementalMastery, "元素精通" },
{ ArtifactAffixType.EnergyRecharge, "元素充能效率" },
{ ArtifactAffixType.HealingBonus, "治疗加成" },
{ ArtifactAffixType.PhysicalDMGBonus, "物理伤害加成" },
{ ArtifactAffixType.PyroDMGBonus, "火元素伤害加成" },
{ ArtifactAffixType.HydroDMGBonus, "水元素伤害加成" },
{ ArtifactAffixType.DendroDMGBonus, "草元素伤害加成" },
{ ArtifactAffixType.ElectroDMGBonus, "雷元素伤害加成" },
{ ArtifactAffixType.AnemoDMGBonus, "风元素伤害加成" },
{ ArtifactAffixType.CryoDMGBonus, "冰元素伤害加成" },
{ ArtifactAffixType.GeoDMGBonus, "岩元素伤害加成" }
}.ToFrozenDictionary();
}
public enum ArtifactAffixType
{
ATK,
ATKPercent,
DEF,
DEFPercent,
HP,
HPPercent,
CRITRate,
CRITDMG,
ElementalMastery,
EnergyRecharge,
HealingBonus,
PhysicalDMGBonus,
/// <summary>
/// 火
/// </summary>
PyroDMGBonus,
/// <summary>
/// 水
/// </summary>
HydroDMGBonus,
/// <summary>
/// 草
/// </summary>
DendroDMGBonus,
/// <summary>
/// 雷
/// </summary>
ElectroDMGBonus,
/// <summary>
/// 风
/// </summary>
AnemoDMGBonus,
/// <summary>
/// 冰
/// </summary>
CryoDMGBonus,
/// <summary>
/// 岩
/// </summary>
GeoDMGBonus,
}
}

View File

@@ -0,0 +1,38 @@
namespace BetterGenshinImpact.GameTask.AutoArtifactSalvage
{
/// <summary>
/// 圣遗物数值面板信息
/// </summary>
public class ArtifactStat
{
public ArtifactStat(string name, ArtifactAffix mainAffix, ArtifactAffix[] minorAffix, int level)
{
Name = name;
MainAffix = mainAffix;
MinorAffixes = minorAffix;
Level = level;
}
/// <summary>
/// 名称
/// </summary>
public string Name { get; private set; }
/// <summary>
/// 主词条
/// </summary>
public ArtifactAffix MainAffix { get; private set; }
/// <summary>
/// 副词条数组
/// </summary>
public ArtifactAffix[] MinorAffixes { get; private set; }
/// <summary>
/// 等级
/// </summary>
public int Level { get; private set; }
// PS圣遗物的种类和品质在点击查看之前就可以通过识别图标获悉所以不必在此模型类中获取
}
}

View File

@@ -1,4 +1,4 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
namespace BetterGenshinImpact.GameTask.AutoArtifactSalvage;
@@ -6,7 +6,17 @@ namespace BetterGenshinImpact.GameTask.AutoArtifactSalvage;
[Serializable]
public partial class AutoArtifactSalvageConfig : ObservableObject
{
// JavaScript
[ObservableProperty]
private string _javaScript =
@"(async function (artifact) {
var hasATK = Array.from(artifact.MinorAffixes).some(affix => affix.Type == 'ATK');
var hasDEF = Array.from(artifact.MinorAffixes).some(affix => affix.Type == 'DEF');
Output = hasATK && hasDEF;
})(ArtifactStat);";
// 正则表达式
[Obsolete]
[ObservableProperty]
private string _regularExpression = @"(?=[\S\s]*攻击力\+[\d]*\n)(?=[\S\s]*防御力\+[\d]*\n)";

View File

@@ -1,30 +1,32 @@
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.Model.GameUI;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Helpers.Extensions;
using Fischless.WindowsInput;
using Microsoft.ClearScript;
using Microsoft.ClearScript.V8;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.Helpers.Extensions;
using Microsoft.Extensions.Logging;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using BetterGenshinImpact.Core.Simulator.Extensions;
using Microsoft.Extensions.Localization;
using System.Globalization;
using BetterGenshinImpact.Helpers;
using System.Text.RegularExpressions;
using BetterGenshinImpact.GameTask.Model.Area;
using System.Collections.Generic;
using Fischless.WindowsInput;
using OpenCvSharp;
using System.Linq;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.Model.GameUI;
namespace BetterGenshinImpact.GameTask.AutoArtifactSalvage;
@@ -46,19 +48,21 @@ public class AutoArtifactSalvageTask : ISoloTask
private readonly string[] numOfStarLocalizedString;
private readonly string? regularExpression;
private readonly string? javaScript;
private readonly int? maxNumToCheck;
private readonly bool returnToMainUi = true;
public AutoArtifactSalvageTask(int star, string? regularExpression = null, int? maxNumToCheck = null)
private readonly CultureInfo cultureInfo;
public AutoArtifactSalvageTask(int star, string? javaScript = null, int? maxNumToCheck = null)
{
this.star = star;
this.regularExpression = regularExpression;
this.javaScript = javaScript;
this.maxNumToCheck = maxNumToCheck;
IStringLocalizer<AutoArtifactSalvageTask> stringLocalizer = App.GetService<IStringLocalizer<AutoArtifactSalvageTask>>() ?? throw new NullReferenceException();
CultureInfo cultureInfo = new CultureInfo(TaskContext.Instance().Config.OtherConfig.GameCultureInfoName);
this.cultureInfo = new CultureInfo(TaskContext.Instance().Config.OtherConfig.GameCultureInfoName);
quickSelectLocalizedString = stringLocalizer.WithCultureGet(cultureInfo, "快速选择");
numOfStarLocalizedString =
[
@@ -237,7 +241,7 @@ public class AutoArtifactSalvageTask : ISoloTask
{
logger.LogInformation("完成{Star}星圣遗物快速分解", star);
await Delay(400, ct);
if (regularExpression != null)
if (javaScript != null)
{
input.Mouse.LeftButtonClick();
await Delay(1000, ct);
@@ -254,9 +258,9 @@ public class AutoArtifactSalvageTask : ISoloTask
}
// 分解5星
if (regularExpression != null)
if (javaScript != null)
{
await Salvage5Star(this.regularExpression, this.maxNumToCheck ?? throw new ArgumentException($"{nameof(this.maxNumToCheck)}不能为空"));
await Salvage5Star(this.javaScript, this.maxNumToCheck ?? throw new ArgumentException($"{nameof(this.maxNumToCheck)}不能为空"));
logger.LogInformation("筛选完毕,请复查并手动分解");
}
else
@@ -270,7 +274,7 @@ public class AutoArtifactSalvageTask : ISoloTask
}
}
private async Task Salvage5Star(string regularExpression, int maxNumToCheck)
private async Task Salvage5Star(string javaScript, int maxNumToCheck)
{
int count = maxNumToCheck;
@@ -291,15 +295,15 @@ public class AutoArtifactSalvageTask : ISoloTask
if (GetArtifactStatus(itemRegion1.SrcMat) == ArtifactStatus.Selected)
{
using ImageRegion card = ra1.DeriveCrop(new Rect((int)(ra1.Width * 0.70), (int)(ra1.Width * 0.055), (int)(ra1.Width * 0.24), (int)(ra1.Width * 0.29)));
string affixes = GetArtifactAffixes(card.SrcMat, OcrFactory.Paddle);
ArtifactStat artifact = GetArtifactStat(card.SrcMat, OcrFactory.Paddle, this.cultureInfo, out string allText);
if (IsMatchRegularExpression(affixes, regularExpression, out string msg))
if (IsMatchJavaScript(artifact, javaScript))
{
logger.LogInformation(message: msg);
// logger.LogInformation(message: msg);
}
else
{
itemRegion.Click();
itemRegion.Click(); // 反选取消
await Delay(100, ct);
}
}
@@ -314,6 +318,45 @@ public class AutoArtifactSalvageTask : ISoloTask
}
}
/// <summary>
/// 是否匹配JavaScript的计算结果
/// </summary>
/// <param name="artifact">作为JS入参JS使用“ArtifactStat”获取</param>
/// <param name="javaScript"></param>
/// <param name="engine">由调用者控制生命周期</param>
/// <returns>是否匹配。取JS的“Output”作为出参</returns>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="Exception"></exception>
public static bool IsMatchJavaScript(ArtifactStat artifact, string javaScript)
{
using V8ScriptEngine engine = new V8ScriptEngine(V8ScriptEngineFlags.UseCaseInsensitiveMemberBinding | V8ScriptEngineFlags.DisableGlobalMembers);
try
{
// 传入输入参数
engine.Script.ArtifactStat = artifact;
// 执行JavaScript代码
engine.Execute(javaScript);
// 检查是否有输出
if (!engine.Script.propertyIsEnumerable("Output"))
{
throw new InvalidOperationException("JavaScript没有设置Output输出");
}
if (engine.Script.Output is not bool)
{
throw new InvalidOperationException("JavaScript的Output输出不是布尔类型");
}
return (bool)engine.Script.Output;
}
catch (ScriptEngineException ex)
{
throw new Exception($"JavaScript execution error: {ex.Message}", ex);
}
}
public static bool IsMatchRegularExpression(string affixes, string regularExpression, out string msg)
{
Match match = Regex.Match(affixes, regularExpression);
@@ -336,10 +379,140 @@ public class AutoArtifactSalvageTask : ISoloTask
return match.Success;
}
public static string GetArtifactAffixes(Mat src, IOcrService ocrService)
public static ArtifactStat GetArtifactStat(Mat src, IOcrService ocrService, CultureInfo cultureInfo, out string allText)
{
var ocrResult = ocrService.OcrResult(src);
return ocrResult.Text;
allText = ocrResult.Text;
var lines = ocrResult.Text.Split('\n');
string percentStr = "%";
// 名称
string name = lines[0];
#region
var defaultMainAffix = ArtifactAffix.DefaultStrDic.Select(kvp => kvp.Value).Distinct();
string mainAffixTypeLine = lines.Single(l => defaultMainAffix.Contains(l));
ArtifactAffixType mainAffixType = ArtifactAffix.DefaultStrDic.First(kvp => kvp.Value == mainAffixTypeLine).Key;
string mainAffixValueLine = lines.Select(l =>
{
string pattern = @"^(\d+\.?\d*)(%?)$";
pattern = pattern.Replace("%", percentStr); // 这样一行一行写只是为了IDE能保持正则字符串高亮
Match match = Regex.Match(l, pattern);
if (match.Success)
{
return match.Groups[1].Value;
}
else
{
return null;
}
}).Where(l => l != null).Cast<string>().Single();
if (!float.TryParse(mainAffixValueLine, NumberStyles.Any, cultureInfo, out float value))
{
throw new Exception($"未识别的主词条数值:{mainAffixValueLine}");
}
ArtifactAffix mainAffix = new ArtifactAffix(mainAffixType, value);
#endregion
#region
ArtifactAffix[] minorAffixes = lines.Select(l =>
{
string pattern = @"^[•·]?([^+]+)\+(\d+\.?\d*)(%?)$";
pattern = pattern.Replace("%", percentStr);
Match match = Regex.Match(l, pattern);
if (match.Success)
{
ArtifactAffixType artifactAffixType;
var dic = ArtifactAffix.DefaultStrDic;
if (match.Groups[1].Value.Contains(dic[ArtifactAffixType.ATK]))
{
if (String.IsNullOrEmpty(match.Groups[3].Value))
{
artifactAffixType = ArtifactAffixType.ATK;
}
else
{
artifactAffixType = ArtifactAffixType.ATKPercent;
}
}
else if (match.Groups[1].Value.Contains(dic[ArtifactAffixType.DEF]))
{
if (String.IsNullOrEmpty(match.Groups[3].Value))
{
artifactAffixType = ArtifactAffixType.DEF;
}
else
{
artifactAffixType = ArtifactAffixType.DEFPercent;
}
}
else if (match.Groups[1].Value.Contains(dic[ArtifactAffixType.HP]))
{
if (String.IsNullOrEmpty(match.Groups[3].Value))
{
artifactAffixType = ArtifactAffixType.HP;
}
else
{
artifactAffixType = ArtifactAffixType.HPPercent;
}
}
else if (match.Groups[1].Value.Contains(dic[ArtifactAffixType.CRITRate]))
{
artifactAffixType = ArtifactAffixType.CRITRate;
}
else if (match.Groups[1].Value.Contains(dic[ArtifactAffixType.CRITDMG]))
{
artifactAffixType = ArtifactAffixType.CRITDMG;
}
else if (match.Groups[1].Value.Contains(dic[ArtifactAffixType.ElementalMastery]))
{
artifactAffixType = ArtifactAffixType.ElementalMastery;
}
else if (match.Groups[1].Value.Contains(dic[ArtifactAffixType.EnergyRecharge]))
{
artifactAffixType = ArtifactAffixType.EnergyRecharge;
}
else
{
throw new Exception($"未识别的副词条:{match.Groups[1].Value}");
}
if (!float.TryParse(match.Groups[2].Value, NumberStyles.Any, cultureInfo, out float value))
{
throw new Exception($"未识别的副词条数值:{match.Groups[2].Value}");
}
return new ArtifactAffix(artifactAffixType, value);
}
else
{
return null;
}
}).Where(a => a != null).Cast<ArtifactAffix>().ToArray();
#endregion
#region
string levelLine = lines.Select(l =>
{
string pattern = @"^\+(\d*)$";
Match match = Regex.Match(l, pattern);
if (match.Success)
{
return match.Groups[1].Value;
}
else
{
return null;
}
}).Where(l => l != null).Cast<string>().Single();
if (!int.TryParse(levelLine, out int level) || level < 0 || level > 20)
{
throw new Exception($"未识别的等级:{levelLine}");
}
#endregion
return new ArtifactStat(name, mainAffix, minorAffixes, level);
}
public static ArtifactStatus GetArtifactStatus(Mat src)

View File

@@ -2291,7 +2291,11 @@
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
指定匹配表达式逐一筛选分解支持5星圣遗物
指定匹配表达式逐一筛选分解支持5星圣遗物 -
<Hyperlink Command="{Binding GoToArtifactSalvageUrlCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
点击查看使用教程
</Hyperlink>
</ui:TextBlock>
<controls:TwoStateButton Grid.Row="0"
Grid.RowSpan="2"
@@ -2343,20 +2347,28 @@
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="正则表达式"
TextWrapping="Wrap" />
TextWrapping="Wrap">
JavaScript -
<Hyperlink Command="{Binding GoToArtifactSalvageUrlCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
点击查看使用教程
</Hyperlink>
</ui:TextBlock>
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="只要满足的圣遗物都会被选中"
TextWrapping="Wrap" />
TextWrapping="Wrap">
只要满足的圣遗物都会被选中
<LineBreak/>
JS接受ArtifactStat作为入参应对Output赋值一个布尔值作为返回
</ui:TextBlock>
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="180"
MaxWidth="800"
Margin="0,0,36,0"
Text="{Binding Config.AutoArtifactSalvageConfig.RegularExpression, Mode=TwoWay}"
Text="{Binding Config.AutoArtifactSalvageConfig.JavaScript, Mode=TwoWay}"
TextWrapping="Wrap" Cursor="IBeam" />
</Grid>
<Grid Margin="16">

View File

@@ -1,8 +1,9 @@
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.AutoArtifactSalvage;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Helpers.Extensions;
using System.Globalization;
using System.Windows;
namespace BetterGenshinImpact.View.Windows;
@@ -13,14 +14,14 @@ public partial class OcrDialog
private readonly double yRatio;
private readonly double widthRatio;
private readonly double heightRatio;
private readonly string? regularExpression;
public OcrDialog(double xRatio, double yRatio, double widthRatio, double heightRatio, string title, string? regularExpression = null)
private readonly string? javaScript;
public OcrDialog(double xRatio, double yRatio, double widthRatio, double heightRatio, string title, string? javaScript = null)
{
this.xRatio = xRatio;
this.yRatio = yRatio;
this.widthRatio = widthRatio;
this.heightRatio = heightRatio;
this.regularExpression = regularExpression;
this.javaScript = javaScript;
InitializeComponent();
@@ -36,11 +37,12 @@ public partial class OcrDialog
this.Screenshot.Source = bitmapImage;
this.TxtRecognized.Text = OcrFactory.Paddle.OcrResult(card.SrcMat).Text;
if (this.regularExpression != null)
ArtifactStat artifact = AutoArtifactSalvageTask.GetArtifactStat(card.SrcMat, OcrFactory.Paddle, new CultureInfo(TaskContext.Instance().Config.OtherConfig.GameCultureInfoName), out string allText);
this.TxtRecognized.Text = allText;
if (this.javaScript != null)
{
AutoArtifactSalvageTask.IsMatchRegularExpression(this.TxtRecognized.Text, this.regularExpression, out string msg);
this.RegexResult.Text = msg;
bool isMatch = AutoArtifactSalvageTask.IsMatchJavaScript(artifact, this.javaScript);
this.RegexResult.Text = isMatch ? "匹配" : "不匹配";
}
this.UpdateLayout();
}

View File

@@ -527,14 +527,20 @@ public partial class TaskSettingsPageViewModel : ViewModel
{
SwitchArtifactSalvageEnabled = true;
await new TaskRunner()
.RunSoloTaskAsync(new AutoArtifactSalvageTask(int.Parse(Config.AutoArtifactSalvageConfig.MaxArtifactStar), Config.AutoArtifactSalvageConfig.RegularExpression, Config.AutoArtifactSalvageConfig.MaxNumToCheck));
.RunSoloTaskAsync(new AutoArtifactSalvageTask(int.Parse(Config.AutoArtifactSalvageConfig.MaxArtifactStar), Config.AutoArtifactSalvageConfig.JavaScript, Config.AutoArtifactSalvageConfig.MaxNumToCheck));
SwitchArtifactSalvageEnabled = false;
}
[RelayCommand]
private async Task OnGoToArtifactSalvageUrlAsync()
{
await Launcher.LaunchUriAsync(new Uri("https://bettergi.com/feats/task/artifactSalvage.html"));
}
[RelayCommand]
private void OnOpenArtifactSalvageTestOCRWindow()
{
OcrDialog ocrDialog = new OcrDialog(0.70, 0.098, 0.24, 0.52, "圣遗物分解", this.Config.AutoArtifactSalvageConfig.RegularExpression);
OcrDialog ocrDialog = new OcrDialog(0.70, 0.098, 0.24, 0.52, "圣遗物分解", this.Config.AutoArtifactSalvageConfig.JavaScript);
ocrDialog.ShowDialog();
}

View File

@@ -1,13 +1,13 @@
using BetterGenshinImpact.GameTask.AutoArtifactSalvage;
using BetterGenshinImpact.GameTask.AutoArtifactSalvage;
using BetterGenshinImpact.GameTask.Model.GameUI;
using BetterGenshinImpact.UnitTest.CoreTests.RecognitionTests.OCRTests;
using OpenCvSharp;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static BetterGenshinImpact.GameTask.AutoArtifactSalvage.AutoArtifactSalvageTask;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoArtifactSalvageTests
@@ -73,7 +73,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoArtifactSalvageTests
private static ConcurrentDictionary<string, string> PaddleResultDic { get; } = new ConcurrentDictionary<string, string>();
/// <summary>
/// 测试获取分解圣遗物界面右侧圣遗物的词缀等属性,结果应正确
/// 测试获取分解圣遗物界面右侧圣遗物的词缀等属性,使用正则表达式,结果应正确
/// </summary>
/// <param name="screenshot"></param>
[Theory]
@@ -88,15 +88,17 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoArtifactSalvageTests
[InlineData(@"ArtifactAffixes.png", @"攻击力\+[\d]*\n", false)]
[InlineData(@"ArtifactAffixes.png", @"防御力\+[\d.]*%\n", false)]
[InlineData(@"ArtifactAffixes.png", @"防御力\+[\d]*\n")]
public void GetArtifactAffixes_ShouldBeRight(string screenshot, string pattern, bool isMatch = true)
public void GetArtifactStat_RegexPatternShouldBeRight(string screenshot, string pattern, bool isMatch = true)
{
//
CultureInfo cultureInfo = new CultureInfo("zh-Hans");
//
string result = PaddleResultDic.GetOrAdd(screenshot, screenshot_ =>
{
using Mat mat = new Mat(@$"..\..\..\Assets\AutoArtifactSalvage\{screenshot_}");
return AutoArtifactSalvageTask.GetArtifactAffixes(mat, paddle.Get());
GetArtifactStat(mat, paddle.Get(), cultureInfo, out string allText);
return allText;
});
//
@@ -109,5 +111,57 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoArtifactSalvageTests
Assert.DoesNotMatch(pattern, result);
}
}
/// <summary>
/// 测试获取分解圣遗物界面右侧圣遗物的各种结构化信息,结果应正确
/// </summary>
/// <param name="screenshot"></param>
[Theory]
[InlineData(@"ArtifactAffixes.png")]
public void GetArtifactStat_AffixesShouldBeRight(string screenshot)
{
//
CultureInfo cultureInfo = new CultureInfo("zh-Hans");
//
using Mat mat = new Mat(@$"..\..\..\Assets\AutoArtifactSalvage\{screenshot}");
ArtifactStat artifact = GetArtifactStat(mat, paddle.Get(), cultureInfo, out string _);
//
Assert.Equal("异种的期许", artifact.Name);
Assert.True(artifact.MainAffix.Type == ArtifactAffixType.HP);
Assert.True(artifact.MainAffix.Value == 717f);
Assert.Contains(artifact.MinorAffixes, a => a.Type == ArtifactAffixType.ElementalMastery && a.Value == 16f);
Assert.Contains(artifact.MinorAffixes, a => a.Type == ArtifactAffixType.EnergyRecharge && a.Value == 6.5f);
Assert.Contains(artifact.MinorAffixes, a => a.Type == ArtifactAffixType.ATKPercent && a.Value == 5.8f);
Assert.Contains(artifact.MinorAffixes, a => a.Type == ArtifactAffixType.DEF && a.Value == 23f);
Assert.True(artifact.Level == 0);
}
[Theory]
[InlineData(@"ArtifactAffixes.png", @"(async function (artifact) {
var hasATK = Array.from(artifact.MinorAffixes).some(affix => affix.Type == 'ATK');
var hasDEF = Array.from(artifact.MinorAffixes).some(affix => affix.Type == 'DEF');
Output = hasATK && hasDEF;
})(ArtifactStat);", false)]
[InlineData(@"ArtifactAffixes.png", @"(async function (artifact) {
var level = artifact.Level;
var hasATKPercent = Array.from(artifact.MinorAffixes).some(affix => affix.Type == 'ATKPercent');
var hasDEF = Array.from(artifact.MinorAffixes).some(affix => affix.Type == 'DEF');
Output = level == 0 && hasATKPercent && hasDEF;
})(ArtifactStat);", true)]
public void IsMatchJavaScript_JSShouldBeRight(string screenshot, string js, bool expected)
{
//
CultureInfo cultureInfo = new CultureInfo("zh-Hans");
//
using Mat mat = new Mat(@$"..\..\..\Assets\AutoArtifactSalvage\{screenshot}");
ArtifactStat artifact = GetArtifactStat(mat, paddle.Get(), cultureInfo, out string _);
bool result = IsMatchJavaScript(artifact, js);
//
Assert.Equal(expected, result);
}
}
}