From 2753a81c1faab363d8e1351c9c59fb2a5f7874f7 Mon Sep 17 00:00:00 2001 From: FishmanTheMurloc <162452111+FishmanTheMurloc@users.noreply.github.com> Date: Fri, 22 Aug 2025 10:31:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9C=A3=E9=81=97=E7=89=A9=E5=88=86=E8=A7=A3?= =?UTF-8?q?=E7=AD=9B=E9=80=89=E6=94=B9=E7=94=A8JS=20(#2087)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AutoArtifactSalvage/ArtifactAffix.cs | 85 ++++++ .../AutoArtifactSalvage/ArtifactStat.cs | 38 +++ .../AutoArtifactSalvageConfig.cs | 12 +- .../AutoArtifactSalvageTask.cs | 241 +++++++++++++++--- .../View/Pages/TaskSettingsPage.xaml | 24 +- .../View/Windows/OcrDialog.xaml.cs | 20 +- .../Pages/TaskSettingsPageViewModel.cs | 10 +- .../AutoArtifactSalvageTaskTests.cs | 64 ++++- 8 files changed, 437 insertions(+), 57 deletions(-) create mode 100644 BetterGenshinImpact/GameTask/AutoArtifactSalvage/ArtifactAffix.cs create mode 100644 BetterGenshinImpact/GameTask/AutoArtifactSalvage/ArtifactStat.cs 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); + } } }