mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-03-15 07:43:20 +08:00
圣遗物分解筛选改用JS (#2087)
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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:圣遗物的种类和品质在点击查看之前就可以通过识别图标获悉,所以不必在此模型类中获取
|
||||
}
|
||||
}
|
||||
@@ -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)";
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user