diff --git a/BetterGenshinImpact/GameTask/AutoArtifactSalvage/ArtifactAffix.cs b/BetterGenshinImpact/GameTask/AutoArtifactSalvage/ArtifactAffix.cs
new file mode 100644
index 00000000..3eb436fc
--- /dev/null
+++ b/BetterGenshinImpact/GameTask/AutoArtifactSalvage/ArtifactAffix.cs
@@ -0,0 +1,85 @@
+using System.Collections.Frozen;
+using System.Collections.Generic;
+
+namespace BetterGenshinImpact.GameTask.AutoArtifactSalvage
+{
+ ///
+ /// 圣遗物词条
+ ///
+ 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 DefaultStrDic { get; } = new Dictionary() {
+ { 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,
+ ///
+ /// 火
+ ///
+ PyroDMGBonus,
+ ///
+ /// 水
+ ///
+ HydroDMGBonus,
+ ///
+ /// 草
+ ///
+ DendroDMGBonus,
+ ///
+ /// 雷
+ ///
+ ElectroDMGBonus,
+ ///
+ /// 风
+ ///
+ AnemoDMGBonus,
+ ///
+ /// 冰
+ ///
+ CryoDMGBonus,
+ ///
+ /// 岩
+ ///
+ GeoDMGBonus,
+ }
+}
diff --git a/BetterGenshinImpact/GameTask/AutoArtifactSalvage/ArtifactStat.cs b/BetterGenshinImpact/GameTask/AutoArtifactSalvage/ArtifactStat.cs
new file mode 100644
index 00000000..214c6887
--- /dev/null
+++ b/BetterGenshinImpact/GameTask/AutoArtifactSalvage/ArtifactStat.cs
@@ -0,0 +1,38 @@
+namespace BetterGenshinImpact.GameTask.AutoArtifactSalvage
+{
+ ///
+ /// 圣遗物数值面板信息
+ ///
+ public class ArtifactStat
+ {
+ public ArtifactStat(string name, ArtifactAffix mainAffix, ArtifactAffix[] minorAffix, int level)
+ {
+ Name = name;
+ MainAffix = mainAffix;
+ MinorAffixes = minorAffix;
+ Level = level;
+ }
+
+ ///
+ /// 名称
+ ///
+ public string Name { get; private set; }
+
+ ///
+ /// 主词条
+ ///
+ public ArtifactAffix MainAffix { get; private set; }
+
+ ///
+ /// 副词条数组
+ ///
+ public ArtifactAffix[] MinorAffixes { get; private set; }
+
+ ///
+ /// 等级
+ ///
+ public int Level { get; private set; }
+
+ // PS:圣遗物的种类和品质在点击查看之前就可以通过识别图标获悉,所以不必在此模型类中获取
+ }
+}
diff --git a/BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageConfig.cs b/BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageConfig.cs
index f4ea4604..28a61138 100644
--- a/BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageConfig.cs
+++ b/BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageConfig.cs
@@ -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)";
diff --git a/BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageTask.cs b/BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageTask.cs
index ca5f1737..f5289b88 100644
--- a/BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageTask.cs
+++ b/BetterGenshinImpact/GameTask/AutoArtifactSalvage/AutoArtifactSalvageTask.cs
@@ -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 stringLocalizer = App.GetService>() ?? 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
}
}
+ ///
+ /// 是否匹配JavaScript的计算结果
+ ///
+ /// 作为JS入参,JS使用“ArtifactStat”获取
+ ///
+ /// 由调用者控制生命周期
+ /// 是否匹配。取JS的“Output”作为出参
+ ///
+ ///
+ 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().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().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().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)
diff --git a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml
index 7a3e242a..3d10b165 100644
--- a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml
+++ b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml
@@ -2291,7 +2291,11 @@
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
- 指定匹配表达式逐一筛选分解,支持5星圣遗物
+ 指定匹配表达式逐一筛选分解,支持5星圣遗物 -
+
+ 点击查看使用教程
+
+ TextWrapping="Wrap">
+ JavaScript -
+
+ 点击查看使用教程
+
+
+ TextWrapping="Wrap">
+ 只要满足的圣遗物都会被选中
+
+ JS接受ArtifactStat作为入参;应对Output赋值一个布尔值作为返回
+
diff --git a/BetterGenshinImpact/View/Windows/OcrDialog.xaml.cs b/BetterGenshinImpact/View/Windows/OcrDialog.xaml.cs
index fef67b47..2da7f5a7 100644
--- a/BetterGenshinImpact/View/Windows/OcrDialog.xaml.cs
+++ b/BetterGenshinImpact/View/Windows/OcrDialog.xaml.cs
@@ -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();
}
diff --git a/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs
index 82a42a8b..19e1901c 100644
--- a/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/TaskSettingsPageViewModel.cs
@@ -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();
}
diff --git a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoArtifactSalvageTests/AutoArtifactSalvageTaskTests.cs b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoArtifactSalvageTests/AutoArtifactSalvageTaskTests.cs
index 3ccd7ffb..6aca25ca 100644
--- a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoArtifactSalvageTests/AutoArtifactSalvageTaskTests.cs
+++ b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoArtifactSalvageTests/AutoArtifactSalvageTaskTests.cs
@@ -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 PaddleResultDic { get; } = new ConcurrentDictionary();
///
- /// 测试获取分解圣遗物界面右侧圣遗物的词缀等属性,结果应正确
+ /// 测试获取分解圣遗物界面右侧圣遗物的词缀等属性,使用正则表达式,结果应正确
///
///
[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);
}
}
+
+ ///
+ /// 测试获取分解圣遗物界面右侧圣遗物的各种结构化信息,结果应正确
+ ///
+ ///
+ [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);
+ }
}
}