自动使用兑换码 (#1895)

This commit is contained in:
辉鸭蛋
2025-07-20 20:24:00 +08:00
committed by GitHub
parent 26d8c9863e
commit 0cec1aecfa
19 changed files with 1214 additions and 167 deletions

View File

@@ -158,6 +158,9 @@
<None Update="GameTask\QuickSereniteaPot\Assets\1920x1080\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="GameTask\UseRedeemCode\Assets\1920x1080\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="User\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
@@ -171,6 +174,7 @@
<ItemGroup>
<Folder Include="GameTask\OneDragon\" />
<Folder Include="GameTask\UseRedeemCode\Assets\1920x1080\" />
<Folder Include="Resources\" />
<Folder Include="Service\Notification\Builder\" />
<Folder Include="User\AutoPathing\" />

View File

@@ -0,0 +1,31 @@
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.GameTask;
using OpenCvSharp;
namespace BetterGenshinImpact.Core.BgiVision;
public class BvImage
{
public RecognitionObject RecognitionObject { get; }
public BvImage(string templateAssert, Rect roi = default, double threshold = 0.8)
{
var args = templateAssert.Split(':');
var featureName = args[0];
var assertName = args[1];
RecognitionObject = new RecognitionObject
{
Name = templateAssert,
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage(featureName, assertName),
RegionOfInterest = roi,
Threshold = threshold
}.InitTemplate();
}
public RecognitionObject ToRecognitionObject()
{
return RecognitionObject;
}
}

View File

@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Model.Area;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
namespace BetterGenshinImpact.Core.BgiVision;
/// <summary>
/// 针对 Region 体系的包装
/// </summary>
public class BvLocator
{
private static readonly ILogger Logger = App.GetLogger<BvLocator>();
private readonly CancellationToken _cancellationToken;
public RecognitionObject RecognitionObject { get; }
public Action? RetryAction { get; set; }
public static int DefaultTimeout { get; set; } = 10000;
public static int DefaultRetryInterval { get; set; } = 250;
public BvLocator(RecognitionObject recognitionObject, CancellationToken cancellationToken)
{
RecognitionObject = recognitionObject;
_cancellationToken = cancellationToken;
}
/// <summary>
/// 根据传入的位置信息定位元素
/// 不建议外部调用使用
/// </summary>
/// <returns></returns>
/// <exception cref="NotSupportedException"></exception>
public List<Region> FindAll()
{
using var screen = CaptureToRectArea();
if (RecognitionObject.RecognitionType == RecognitionTypes.TemplateMatch)
{
var region = screen.Find(RecognitionObject);
if (region.IsExist())
{
return [region];
}
return [];
}
else if (RecognitionObject.RecognitionType == RecognitionTypes.Ocr)
{
var results = screen.FindMulti(RecognitionObject);
if (!string.IsNullOrEmpty(RecognitionObject.Text))
{
return results.FindAll(r => r.Text.Contains(RecognitionObject.Text));
}
return results;
}
else
{
throw new NotSupportedException($"不被 Locator 支持的识别类型: {RecognitionObject.RecognitionType}");
}
}
public bool IsExist()
{
return FindAll().Count > 0;
}
public async Task<Region> Click(int? timeout = null)
{
return (await WaitFor(timeout)).First().Click();
}
public async Task<Region> DoubleClick(int? timeout = null)
{
var list = await WaitFor(timeout);
return list.First().DoubleClick();
}
public async Task<List<Region>> WaitFor(int? timeout = null)
{
var actualTimeout = timeout ?? DefaultTimeout;
var retryCount = actualTimeout / DefaultRetryInterval;
List<Region> results = [];
var retryRes = await NewRetry.WaitForAction(() =>
{
RetryAction?.Invoke();
results = FindAll();
return results.Count > 0;
}, _cancellationToken, retryCount, DefaultRetryInterval);
if (retryRes)
{
return results;
}
else
{
throw new TimeoutException($"识别元素在 {actualTimeout}ms 后超时未出现!");
}
}
public async Task<List<Region>> TryWaitFor(int? timeout = null)
{
try
{
return await WaitFor(timeout);
}
catch
{
return [];
}
}
public async Task WaitForDisappear(int? timeout = null)
{
var actualTimeout = timeout ?? DefaultTimeout;
var retryCount = actualTimeout / DefaultRetryInterval;
var retryRes = await NewRetry.WaitForAction(() =>
{
RetryAction?.Invoke();
var results = FindAll();
return results.Count == 0;
}, _cancellationToken, retryCount, DefaultRetryInterval);
if (!retryRes)
{
throw new TimeoutException($"识别元素在 {actualTimeout}ms 后超时未消失!");
}
}
public async Task TryWaitForDisappear(int? timeout = null)
{
try
{
await WaitForDisappear(timeout);
}
catch
{
// ignored
}
}
/// <summary>
/// 方便优雅的设置感兴趣区域 (ROI)
/// 该方法会覆盖 RecognitionObject.RegionOfInterest 的值。
/// </summary>
/// <param name="rect"></param>
/// <returns></returns>
public BvLocator WithRoi(Rect rect)
{
RecognitionObject.RegionOfInterest = rect;
return this;
}
public BvLocator WithRetryAction(Action? action)
{
RetryAction = action;
return this;
}
}

View File

@@ -0,0 +1,121 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Model.Area;
using Fischless.WindowsInput;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
namespace BetterGenshinImpact.Core.BgiVision;
public class BvPage
{
private static readonly ILogger Logger = App.GetLogger<BvPage>();
private readonly CancellationToken _cancellationToken;
public IKeyboardSimulator Keyboard => Simulation.SendInput.Keyboard;
public IMouseSimulator Mouse => Simulation.SendInput.Mouse;
/// <summary>
/// Default timeout for operations in milliseconds
/// </summary>
public int DefaultTimeout { get; set; } = 10000;
/// <summary>
/// Default retry interval in milliseconds
/// </summary>
public int DefaultRetryInterval { get; set; } = 1000;
public BvPage(CancellationToken cancellationToken = default)
{
_cancellationToken = cancellationToken;
}
/// <summary>
/// 截图
/// </summary>
/// <returns></returns>
public ImageRegion Screenshot()
{
return TaskControl.CaptureToRectArea();
}
/// <summary>
/// 等待
/// </summary>
/// <param name="milliseconds"></param>
/// <returns></returns>
public async Task<BvPage> Wait(int milliseconds)
{
await TaskControl.Delay(milliseconds, _cancellationToken);
return this;
}
/// <summary>
/// 定位图片位置
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
public BvLocator Locator(BvImage image)
{
return new BvLocator(image.ToRecognitionObject(), _cancellationToken);
}
/// <summary>
/// 定位文本位置
/// </summary>
/// <param name="text"></param>
/// <param name="rect"></param>
/// <returns></returns>
public BvLocator Locator(string text, Rect rect = default)
{
return Locator(new RecognitionObject
{
RecognitionType = RecognitionTypes.Ocr,
RegionOfInterest = rect,
Text = text
});
}
/// <summary>
/// 定位 RecognitionObject 代表的位置
/// </summary>
/// <param name="ro"></param>
/// <returns></returns>
public BvLocator Locator(RecognitionObject ro)
{
return new BvLocator(ro, _cancellationToken);
}
public BvLocator GetByText(string text = "", Rect rect = default)
{
return Locator(text, rect);
}
public BvLocator GetByImage(BvImage image)
{
return Locator(image);
}
public List<Region> Ocr(Rect rect = default)
{
return Locator(string.Empty, rect).FindAll();
}
/// <summary>
/// 1080P 分辨率下点击坐标
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
public void Click(double x, double y)
{
GameCaptureRegion.GameRegion1080PPosClick(x, y);
}
}

View File

@@ -167,24 +167,29 @@ public class RecognitionObject
public Dictionary<string, string[]> ReplaceDictionary { get; set; } = [];
/// <summary>
/// 包含匹配
/// 包含匹配 (用于单个确认是否存在)
/// 多个值全匹配的情况下才算成功
/// 复杂情况请用下面的正则匹配
/// </summary>
public List<string> AllContainMatchText { get; set; } = [];
/// <summary>
/// 包含匹配
/// 包含匹配(用于单个确认是否存在)
/// 一个值匹配就算成功
/// </summary>
public List<string> OneContainMatchText { get; set; } = [];
/// <summary>
/// 正则匹配
/// 正则匹配(用于单个确认是否存在)
/// 多个值全匹配的情况下才算成功
/// </summary>
public List<string> RegexMatchText { get; set; } = [];
/// <summary>
/// 用于多个OCR结果的匹配
/// </summary>
public string Text { get; set; } = string.Empty;
public static RecognitionObject Ocr(double x, double y, double w, double h)
{
return new RecognitionObject

View File

@@ -407,16 +407,12 @@ public class ScriptRepoUpdater : Singleton<ScriptRepoUpdater>
ZipFile.ExtractToDirectory(zipPath, ReposPath, true);
}
public async Task ImportScriptFromClipboard()
public async Task ImportScriptFromClipboard( string clipboardText )
{
// 获取剪切板内容
try
{
if (Clipboard.ContainsText())
{
string clipboardText = Clipboard.GetText();
await ImportScriptFromUri(clipboardText, true);
}
await ImportScriptFromUri(clipboardText, true);
}
catch (Exception e)
{

View File

@@ -6,6 +6,7 @@ using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
using BetterGenshinImpact.GameTask.Common;
using Vanara.PInvoke;
namespace BetterGenshinImpact.GameTask.Model.Area;
@@ -100,10 +101,20 @@ public class Region : IDisposable
/// 点击【自己】的中心
/// region.Derive(x,y).Click() 等效于 region.ClickTo(x,y)
/// </summary>
public void Click()
public Region Click()
{
// 相对自己是 0, 0 坐标
ClickTo(0, 0, Width, Height);
return this;
}
public Region DoubleClick()
{
// 相对自己是 0, 0 坐标
ClickTo(0, 0, Width, Height);
TaskControl.Sleep(60);
ClickTo(0, 0, Width, Height);
return this;
}
/// <summary>

View File

@@ -1,6 +0,0 @@
namespace BetterGenshinImpact.GameTask.UseActiveCode
{
internal class UseActiveCodeTask
{
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.UseRedeemCode.Model;
using BetterGenshinImpact.Helpers.Http;
using Newtonsoft.Json.Linq;
namespace BetterGenshinImpact.GameTask.UseRedeemCode;
/// <summary>
/// 获取直播的前瞻兑换码
/// </summary>
public class GetLiveRedeemCode
{
private static readonly string BBS_URL = "https://bbs-api.mihoyo.com";
private readonly HttpClient _httpClient = HttpClientFactory.GetCommonSendClient();
private readonly Dictionary<string, string> _url = new()
{
{ "act_id_1", $"{BBS_URL}/painter/api/user_instant/list?offset=0&size=20&uid=75276539" },
{ "act_id_2", $"{BBS_URL}/painter/api/user_instant/list?offset=0&size=20&uid=75276550" },
{ "index", "https://api-takumi.mihoyo.com/event/miyolive/index" },
{ "code", "https://api-takumi-static.mihoyo.com/event/miyolive/refreshCode" }
};
private async Task<JObject> GetDataAsync(string type, Dictionary<string, string>? data = null)
{
try
{
HttpResponseMessage res;
var request = new HttpRequestMessage(HttpMethod.Get, _url[type]);
// 为所有需要的请求添加 act_id header
if ((type == "index" || type == "code") && data != null && data.TryGetValue("actId", out var actId))
{
request.Headers.Add("x-rpc-act_id", actId);
}
// 为code类型添加查询参数
if (type == "code" && data != null)
{
var uriBuilder = new UriBuilder(_url[type]);
var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query);
query["version"] = data.GetValueOrDefault("version", "");
query["time"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
uriBuilder.Query = query.ToString();
request.RequestUri = uriBuilder.Uri;
}
res = await _httpClient.SendAsync(request);
var content = await res.Content.ReadAsStringAsync();
return JObject.Parse(content);
}
catch (Exception e)
{
return JObject.Parse($"{{\"error\":\"[{e.GetType().Name}] {type} 接口请求错误\",\"retcode\":1}}");
}
}
private async Task<string> GetActIdAsync(string id)
{
var ret = await GetDataAsync($"act_id_{id}");
if (ret == null) return "";
// 检查error或retcode != 0
if (ret["error"] != null || ret["retcode"]?.Value<int>() != 0)
return "";
string actId = "";
var keywords = new List<string> { "前瞻特别节目" };
var list = ret["data"]?["list"] as JArray;
if (list == null) return "";
foreach (var p in list)
{
var post = p["post"]?["post"];
if (post == null) continue;
var subject = post["subject"]?.Value<string>();
if (string.IsNullOrEmpty(subject)) continue;
bool containsAll = keywords.All(word => subject.Contains(word));
if (!containsAll) continue;
var structContent = post["structured_content"]?.Value<string>();
if (string.IsNullOrEmpty(structContent)) continue;
var segments = JArray.Parse(structContent);
foreach (var segment in segments)
{
var link = segment["attributes"]?["link"]?.Value<string>() ?? "";
// 优化安全获取insert字段值
string insert = "";
var insertToken = segment["insert"];
if (insertToken != null)
{
if (insertToken.Type == JTokenType.String)
{
insert = insertToken.Value<string>() ?? "";
}
else if (insertToken.Type == JTokenType.Object)
{
// 如果是对象,尝试转换为字符串
insert = insertToken.ToString();
}
}
if ((insert.Contains("观看") || insert.Contains("米游社直播间")) && !string.IsNullOrEmpty(link))
{
var match = Regex.Match(link, @"act_id=(.*?)\&");
if (match.Success)
actId = match.Groups[1].Value;
}
}
if (!string.IsNullOrEmpty(actId)) break;
}
return actId;
}
private async Task<(string codeVer, string title)> GetLiveDataAsync(string actId)
{
var ret = await GetDataAsync("index", new Dictionary<string, string> { { "actId", actId } });
if (ret == null || ret["error"] != null || ret["retcode"]?.Value<int>() != 0)
return (null, null);
var liveRaw = ret["data"]?["live"];
if (liveRaw == null) return (null, null);
string codeVer = liveRaw["code_ver"]?.Value<string>();
string title = liveRaw["title"]?.Value<string>();
return (codeVer, title);
}
private async Task<List<RedeemCode>> GetCodeAsync(string version, string actId)
{
var ret = await GetDataAsync("code", new Dictionary<string, string> { { "version", version }, { "actId", actId } });
var result = new List<RedeemCode>();
if (ret == null || ret["error"] != null || ret["retcode"]?.Value<int>() != 0)
return result;
var removeTag = new Regex("<.*?>", RegexOptions.Compiled);
var codeList = ret["data"]?["code_list"] as JArray;
if (codeList == null) return result;
foreach (var codeInfo in codeList)
{
string items = removeTag.Replace(codeInfo["title"]?.Value<string>() ?? "", "");
string code = codeInfo["code"]?.Value<string>();
if (!string.IsNullOrEmpty(code))
result.Add(new RedeemCode(code, items));
}
return result;
}
/// <summary>
/// 获取前瞻直播兑换码信息。返回格式List[("奖励内容", "兑换码")]
/// </summary>
public async Task<List<RedeemCode>> GetCodeMsgAsync()
{
string actId = await GetActIdAsync("1");
if (string.IsNullOrEmpty(actId))
{
actId = await GetActIdAsync("2");
if (string.IsNullOrEmpty(actId))
throw new Exception("暂无前瞻直播资讯!");
}
var (codeVer, title) = await GetLiveDataAsync(actId);
if (string.IsNullOrEmpty(codeVer))
throw new Exception("前瞻直播数据异常");
var codeList = await GetCodeAsync(codeVer, actId);
return codeList;
}
}

View File

@@ -0,0 +1,14 @@
namespace BetterGenshinImpact.GameTask.UseRedeemCode.Model;
public class RedeemCode
{
public string Code { get; set; }
public string? Items { get; set; }
public RedeemCode(string code, string? items)
{
Code = code;
Items = items;
}
}

View File

@@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using BetterGenshinImpact.View.Windows;
using TextBox = Wpf.Ui.Controls.TextBox;
namespace BetterGenshinImpact.GameTask.UseRedeemCode;
public class RedeemCodeManager
{
public static async Task ImportFromClipboard(string clipboardText)
{
var codes = ExtractAllCodes(clipboardText);
if (codes.Count == 0)
{
return;
}
var multilineTextBox = new TextBox
{
TextWrapping = TextWrapping.Wrap,
Height = 340,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
Text = codes.Aggregate("", (current, code) => current + $"{code}\n"),
};
var p = new PromptDialog(
"从剪切版中获取到下面的兑换码,是否自动使用?",
"自动使用兑换码",
multilineTextBox,
null);
p.Height = 500;
p.ShowDialog();
if (p.DialogResult != true)
{
return;
}
Clipboard.Clear();
await new TaskRunner().RunSoloTaskAsync(new UseRedemptionCodeTask(codes));
}
public static List<string> ExtractAllCodes(string clipboardText)
{
if (string.IsNullOrEmpty(clipboardText))
{
return [];
}
var regex = new Regex(@"(?<![A-Z0-9])(?=[A-Z0-9]*[A-Z])[A-Z0-9]{12}(?![A-Z0-9])");
return regex.Matches(clipboardText)
.Select(m => m.Value)
.ToList();
}
}

View File

@@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using BetterGenshinImpact.Core.BgiVision;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.GameTask.UseRedeemCode.Model;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Helpers.Extensions;
using Microsoft.Extensions.Logging;
using Vanara.PInvoke;
using Rect = OpenCvSharp.Rect;
namespace BetterGenshinImpact.GameTask.UseRedeemCode;
public class UseRedemptionCodeTask : ISoloTask
{
private static readonly ILogger _logger = App.GetLogger<UseRedemptionCodeTask>();
private readonly List<RedeemCode> _list;
public UseRedemptionCodeTask(List<RedeemCode> list)
{
this._list = list;
}
public UseRedemptionCodeTask(List<string> strList)
{
_list = strList
.Where(code => !string.IsNullOrWhiteSpace(code))
.Select(code => new RedeemCode(code, null))
.ToList();
}
public string Name => "使用兑换码";
public async Task Start(CancellationToken ct)
{
InitLog(_list);
try
{
Rect captureRect = TaskContext.Instance().SystemInfo.ScaleMax1080PCaptureRect;
await new ReturnMainUiTask().Start(ct);
var page = new BvPage(ct);
_logger.LogInformation("使用兑换码: {Msg}", "打开设置");
// 按ESC键打开菜单
page.Keyboard.KeyPress(User32.VK.VK_ESCAPE);
// 等待ESC后菜单出现
await page.Locator(new BvImage("UseRedeemCode:esc_return_button.png")).WaitFor();
// 点击设置按钮
page.Click(45, 825);
await page.Wait(1000);
// 点击账户
_logger.LogInformation("使用兑换码: {Msg}", "点击账户 —— 前往兑换");
await page.GetByText("账户").WithRoi(captureRect.CutLeft(0.2)).Click();
await page.Wait(300);
// 点击前往兑换
await page.GetByText("前往兑换").WithRoi(captureRect.CutRight(0.3)).Click();
// 等待兑换码输入框出现
await page.GetByText("兑换奖励").WaitFor();
foreach (var redeemCode in _list)
{
if (string.IsNullOrEmpty(redeemCode.Code))
{
continue;
}
await UseRedeemCode(redeemCode, page);
}
}
catch (Exception ex)
{
_logger.LogError("使用兑换码时发生错误: {Message}", ex.Message);
_logger.LogDebug(ex, "使用兑换码时发生错误");
}
finally
{
// 清空剪贴板
UIDispatcherHelper.Invoke(Clipboard.Clear);
// 返回主界面
await new ReturnMainUiTask().Start(ct);
}
}
private async Task UseRedeemCode(RedeemCode redeemCode, BvPage page)
{
Rect captureRect = TaskContext.Instance().SystemInfo.ScaleMax1080PCaptureRect;
_logger.LogInformation("输入兑换码: {Code}", redeemCode.Code);
// 将要输入的文本复制到剪贴板
UIDispatcherHelper.Invoke(() => Clipboard.SetDataObject(redeemCode.Code!));
// 粘贴兑换码
await page.GetByText("粘贴").WithRoi(captureRect.CutRight(0.5)).Click();
// 点击兑换
await page.Locator(ElementAssets.Instance.BtnWhiteConfirm).Click();
// 兑换成功
var list = await page.GetByText("兑换成功").TryWaitFor(1000);
if (list.Count > 0)
{
_logger.LogInformation("兑换码 {Code} 兑换成功", redeemCode.Code);
// 点击确认
await page.Locator(ElementAssets.Instance.BtnBlackConfirm).Click();
await page.Wait(5100);
}
else
{
_logger.LogWarning("兑换码 {Code} 兑换失败,可能是过期、错误或已被使用", redeemCode.Code);
// 点击清除
await page.GetByText("清除").WithRoi(captureRect.CutRight(0.5)).Click();
}
}
private static void InitLog(List<RedeemCode> list)
{
_logger.LogInformation("开始使用兑换码:");
foreach (var redeemCode in list)
{
if (string.IsNullOrEmpty(redeemCode.Items))
{
_logger.LogInformation("{Code}", redeemCode.Code);
}
else
{
_logger.LogInformation("{Code} - {Msg}", redeemCode.Code, redeemCode.Items);
}
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Concurrent;
using System.Net.Http;
namespace BetterGenshinImpact.Helpers.Http;
public class HttpClientFactory
{
private static readonly ConcurrentDictionary<string, HttpClient> Clients = new();
public static HttpClient GetClient(string key, Func<HttpClient> factory)
{
return Clients.GetOrAdd(key, _ => factory());
}
public static HttpClient GetCommonSendClient()
{
return Clients.GetOrAdd("common", _ => new HttpClient());
}
}

View File

@@ -677,7 +677,7 @@
</ui:CardExpander>
</Grid>
<Separator Margin="-18,0"
BorderThickness="0,1,0,0"/>
BorderThickness="0,1,0,0" />
<Grid Margin="16,10,52,0">
<ui:CardExpander Margin="0,0,0,12"
@@ -715,29 +715,29 @@
<StackPanel>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="自动拾取掉落物时长"
TextWrapping="Wrap"/>
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="单位为秒。0表示不自动拾取掉落物。"
TextWrapping="Wrap"/>
TextWrapping="Wrap" />
<ui:TextBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
MinWidth="120"
Text="{Binding Config.AutoFightConfig.PickDropsAfterFightSeconds}"/>
Text="{Binding Config.AutoFightConfig.PickDropsAfterFightSeconds}" />
</Grid>
</StackPanel>
</ui:CardExpander>
@@ -963,10 +963,10 @@
Text="优先使用浓缩树脂,然后使用原粹树脂,其余树脂不使用"
TextWrapping="Wrap" />
<ui:ToggleSwitch Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
IsChecked="{Binding Config.AutoDomainConfig.SpecifyResinUse,Converter={StaticResource InverseBooleanConverter}, Mode=TwoWay}" />
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
IsChecked="{Binding Config.AutoDomainConfig.SpecifyResinUse,Converter={StaticResource InverseBooleanConverter}, Mode=TwoWay}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
@@ -1011,12 +1011,13 @@
<ui:TextBlock Text="原粹树脂刷取次数:"
VerticalAlignment="Center"
Margin="0,0,8,0" />
<ui:NumberBox Value="{Binding Config.AutoDomainConfig.OriginalResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120"/>
<ui:NumberBox
Value="{Binding Config.AutoDomainConfig.OriginalResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
</StackPanel>
<!-- 浓缩树脂设置 -->
@@ -1025,12 +1026,13 @@
<ui:TextBlock Text="浓缩树脂刷取次数:"
VerticalAlignment="Center"
Margin="0,0,8,0" />
<ui:NumberBox Value="{Binding Config.AutoDomainConfig.CondensedResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
<ui:NumberBox
Value="{Binding Config.AutoDomainConfig.CondensedResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
</StackPanel>
<!-- 须臾树脂设置 -->
@@ -1039,12 +1041,13 @@
<ui:TextBlock Text="须臾树脂刷取次数:"
VerticalAlignment="Center"
Margin="0,0,8,0" />
<ui:NumberBox Value="{Binding Config.AutoDomainConfig.TransientResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
<ui:NumberBox
Value="{Binding Config.AutoDomainConfig.TransientResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
</StackPanel>
<!-- 脆弱树脂设置 -->
@@ -1053,12 +1056,13 @@
<ui:TextBlock Text="脆弱树脂刷取次数:"
VerticalAlignment="Center"
Margin="0,0,8,0" />
<ui:NumberBox Value="{Binding Config.AutoDomainConfig.FragileResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
<ui:NumberBox
Value="{Binding Config.AutoDomainConfig.FragileResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
</StackPanel>
</StackPanel>
</Border>
@@ -1367,10 +1371,10 @@
Text="优先使用浓缩树脂,然后使用原粹树脂,其余树脂不使用"
TextWrapping="Wrap" />
<ui:ToggleSwitch Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
IsChecked="{Binding Config.AutoStygianOnslaughtConfig.SpecifyResinUse,Converter={StaticResource InverseBooleanConverter}, Mode=TwoWay}" />
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
IsChecked="{Binding Config.AutoStygianOnslaughtConfig.SpecifyResinUse,Converter={StaticResource InverseBooleanConverter}, Mode=TwoWay}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
@@ -1415,12 +1419,13 @@
<ui:TextBlock Text="原粹树脂刷取次数:"
VerticalAlignment="Center"
Margin="0,0,8,0" />
<ui:NumberBox Value="{Binding Config.AutoStygianOnslaughtConfig.OriginalResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120"/>
<ui:NumberBox
Value="{Binding Config.AutoStygianOnslaughtConfig.OriginalResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
</StackPanel>
<!-- 浓缩树脂设置 -->
@@ -1429,12 +1434,13 @@
<ui:TextBlock Text="浓缩树脂刷取次数:"
VerticalAlignment="Center"
Margin="0,0,8,0" />
<ui:NumberBox Value="{Binding Config.AutoStygianOnslaughtConfig.CondensedResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
<ui:NumberBox
Value="{Binding Config.AutoStygianOnslaughtConfig.CondensedResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
</StackPanel>
<!-- 须臾树脂设置 -->
@@ -1443,12 +1449,13 @@
<ui:TextBlock Text="须臾树脂刷取次数:"
VerticalAlignment="Center"
Margin="0,0,8,0" />
<ui:NumberBox Value="{Binding Config.AutoStygianOnslaughtConfig.TransientResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
<ui:NumberBox
Value="{Binding Config.AutoStygianOnslaughtConfig.TransientResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
</StackPanel>
<!-- 脆弱树脂设置 -->
@@ -1457,12 +1464,13 @@
<ui:TextBlock Text="脆弱树脂刷取次数:"
VerticalAlignment="Center"
Margin="0,0,8,0" />
<ui:NumberBox Value="{Binding Config.AutoStygianOnslaughtConfig.FragileResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
<ui:NumberBox
Value="{Binding Config.AutoStygianOnslaughtConfig.FragileResinUseCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Minimum="0"
SmallChange="1"
LargeChange="5"
SpinButtonPlacementMode="Inline"
Width="120" />
</StackPanel>
</StackPanel>
</Border>
@@ -1580,45 +1588,9 @@
</StackPanel>
</ui:CardExpander>-->
<!-- 自动音游 -->
<ui:CardControl Margin="0,0,0,12"
Icon="{ui:SymbolIcon MusicNote224}">
<ui:CardControl.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="自动音游(千音雅集)"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
进入演奏界面使用,下落模式必须选择垂落模式 -
<Hyperlink Command="{Binding GoToAutoMusicGameUrlCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
点击查看使用教程
</Hyperlink>
</ui:TextBlock>
</Grid>
</ui:CardControl.Header>
<controls:TwoStateButton Margin="0,0,40,0"
DisableCommand="{Binding StopSoloTaskCommand}"
DisableContent="停止"
EnableCommand="{Binding SwitchAutoMusicGameCommand}"
EnableContent="{Binding SwitchAutoMusicGameButtonText}"
IsChecked="{Binding SwitchAutoMusicGameEnabled}" />
</ui:CardControl>
<!-- 自动音游专辑 -->
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0">
<ui:CardExpander.Icon>
<ui:FontIcon Glyph="&#xf89f;" Style="{StaticResource FaFontIconStyle}" />
</ui:CardExpander.Icon>
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0"
Icon="{ui:SymbolIcon MusicNote224}">
<ui:CardExpander.Header>
<Grid>
<Grid.RowDefinitions>
@@ -1632,27 +1604,18 @@
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="自动完成专辑下所有乐曲(千音雅集"
Text="自动千音雅集"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
进入专辑界面使用,自动演奏未完成乐曲 -
可以自动演奏单个,也可以全自动完成整个专辑 -
<Hyperlink Command="{Binding GoToAutoMusicGameUrlCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
点击查看使用教程
</Hyperlink>
</ui:TextBlock>
<controls:TwoStateButton Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,24,0"
DisableCommand="{Binding StopSoloTaskCommand}"
DisableContent="停止"
EnableCommand="{Binding SwitchAutoAlbumCommand}"
EnableContent="{Binding SwitchAutoAlbumButtonText}"
IsChecked="{Binding SwitchAutoAlbumEnabled}" />
</Grid>
</ui:CardExpander.Header>
@@ -1669,7 +1632,75 @@
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="自动演奏未达成【大音天籁】的乐曲"
Text="【乐曲】 演奏单个乐曲"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
进入演奏界面使用,下落模式必须选择垂落模式 -
<Hyperlink Command="{Binding GoToAutoMusicGameUrlCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
点击查看使用教程
</Hyperlink>
</ui:TextBlock>
<controls:TwoStateButton Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
DisableCommand="{Binding StopSoloTaskCommand}"
DisableContent="停止"
EnableCommand="{Binding SwitchAutoMusicGameCommand}"
EnableContent="{Binding SwitchAutoMusicGameButtonText}"
IsChecked="{Binding SwitchAutoMusicGameEnabled}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="【专辑】 全自动完成整个专辑"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
进入专辑界面使用,自动演奏未完成乐曲 -
<Hyperlink Command="{Binding GoToAutoMusicGameUrlCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
点击查看使用教程
</Hyperlink>
</ui:TextBlock>
<controls:TwoStateButton Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
DisableCommand="{Binding StopSoloTaskCommand}"
DisableContent="停止"
EnableCommand="{Binding SwitchAutoAlbumCommand}"
EnableContent="{Binding SwitchAutoAlbumButtonText}"
IsChecked="{Binding SwitchAutoAlbumEnabled}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="【专辑】 自动演奏未达成【大音天籁】的乐曲"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
@@ -1694,7 +1725,7 @@
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="自动演奏的目标难度选择"
Text="【专辑】 自动演奏的目标难度选择"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
@@ -1913,12 +1944,199 @@
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap" >
TextWrapping="Wrap">
<Hyperlink Command="{Binding GoToTorchPreviousVersionsCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
下载
</Hyperlink> 到本地后填入torch_cpu.dll或torch_cuda.dll的完整地址。如未生效可尝试重启BGI。
</Hyperlink>
到本地后填入torch_cpu.dll或torch_cuda.dll的完整地址。如未生效可尝试重启BGI。
</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.AutoFishingConfig.TorchDllFullPath, Mode=TwoWay}"
TextWrapping="Wrap" Cursor="IBeam" />
</Grid>
</StackPanel>
</ui:CardExpander>
<!-- 自动使用兑换码 -->
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0" Icon="{ui:SymbolIcon BarcodeScanner24}">
<ui:CardExpander.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="自动使用兑换码"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
自动使用输入的兑换码
</ui:TextBlock>
<controls:TwoStateButton Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,24,0"
DisableCommand="{Binding StopSoloTaskCommand}"
DisableContent="停止"
EnableCommand="{Binding SwitchAutoRedeemCodeCommand}"
EnableContent="{Binding SwitchAutoRedeemCodeButtonText}"
IsChecked="{Binding SwitchAutoRedeemCodeEnabled}" />
</Grid>
</ui:CardExpander.Header>
<StackPanel>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="上钩等待超时时间"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="超过这个时间将自动提竿,并重新识别并选择鱼饵进行抛竿"
TextWrapping="Wrap" />
<ui:NumberBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
Maximum="60"
Minimum="5"
ValidationMode="InvalidInputOverwritten"
Value="{Binding Config.AutoFishingConfig.AutoThrowRodTimeOut, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Config.AutoFishingConfig.AutoThrowRodTimeOut, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="整个任务超时时间"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="超过这个时间将强制结束任务"
TextWrapping="Wrap" />
<ui:NumberBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
Maximum="1800"
Minimum="0"
ValidationMode="InvalidInputOverwritten"
Value="{Binding Config.AutoFishingConfig.WholeProcessTimeoutSeconds, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Config.AutoFishingConfig.WholeProcessTimeoutSeconds, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="昼夜策略"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="钓全天的鱼、还是只钓白天或夜晚的鱼、亦或不调整时间"
TextWrapping="Wrap" />
<ComboBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Width="100"
Margin="0,0,36,0"
ItemsSource="{Binding FishingTimePolicyDict}"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedItem="{Binding Config.AutoFishingConfig.FishingTimePolicy, Converter={StaticResource EnumToKVPConverter}, Mode=TwoWay}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="关键帧保存截图(开发者)"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
Text="在流程判断的关键时刻保存当时的截图,供分析判断。会大量写入,非调试时请关闭。需要启用保存截图功能"
TextWrapping="Wrap" />
<ui:ToggleSwitch Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
IsEnabled="{Binding Config.CommonConfig.ScreenshotEnabled}"
IsChecked="{Binding SaveScreenshotOnKeyTick, Mode=TwoWay}" />
</Grid>
<Grid Margin="16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="torch库文件地址仅限2.5.1版本)"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap">
<Hyperlink Command="{Binding GoToTorchPreviousVersionsCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
下载
</Hyperlink>
到本地后填入torch_cpu.dll或torch_cuda.dll的完整地址。如未生效可尝试重启BGI。
</ui:TextBlock>
<ui:TextBox Grid.Row="0"
@@ -2073,18 +2291,18 @@
Text="达到最大检查数量后也会停止"
TextWrapping="Wrap" />
<ui:NumberBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="90"
Margin="0,0,36,0"
Value="{Binding Config.AutoArtifactSalvageConfig.MaxNumToCheck, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Config.AutoArtifactSalvageConfig.MaxNumToCheck, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="90"
Margin="0,0,36,0"
Value="{Binding Config.AutoArtifactSalvageConfig.MaxNumToCheck, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Config.AutoArtifactSalvageConfig.MaxNumToCheck, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</StackPanel>
</ui:CardExpander>
<!-- 截取物品图标(开发者) -->
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0"
<ui:CardExpander Margin="0,0,0,12" ContentPadding="0"
Visibility="{Binding Config.CommonConfig.ScreenshotEnabled, Converter={StaticResource BooleanToVisibilityConverter}}">
<ui:CardExpander.Icon>
<ui:FontIcon Glyph="&#xf4bb;" Style="{StaticResource FaFontIconStyle}" />
@@ -2097,7 +2315,7 @@
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="183*" />
<ColumnDefinition Width="70*"/>
<ColumnDefinition Width="70*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ui:TextBlock Grid.Row="0"
@@ -2115,11 +2333,11 @@
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
log/gridIcons
</Hyperlink>
<LineBreak/>
<LineBreak />
以下过长的内容在pr时会搬到教程里去
<LineBreak/>
<LineBreak />
需要漆黑的背景以降低干扰,比如渊下宫-蛇肠之路的一个锚点,将视角竖直向上看向洞顶
<LineBreak/>
<LineBreak />
诸如提纳里的耳朵太长了,他装备的物品角标目前无法正确地和正上方的物品图标进行轮廓分割,请手动规避
</ui:TextBlock>
<controls:TwoStateButton Grid.Row="0"
@@ -2236,12 +2454,12 @@
Text="达到最大截取数量后会停止"
TextWrapping="Wrap" />
<ui:NumberBox Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="90"
Margin="0,0,36,0"
Value="{Binding Config.GetGridIconsConfig.MaxNumToGet, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Config.GetGridIconsConfig.MaxNumToGet, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Grid.RowSpan="2"
Grid.Column="1"
MinWidth="90"
Margin="0,0,36,0"
Value="{Binding Config.GetGridIconsConfig.MaxNumToGet, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding Config.GetGridIconsConfig.MaxNumToGet, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</StackPanel>
</ui:CardExpander>

View File

@@ -28,7 +28,7 @@ public partial class PromptDialog
{
private readonly PromptDialogConfig _config;
public PromptDialog(string question, string title, UIElement uiElement, string defaultValue, PromptDialogConfig? config = null)
public PromptDialog(string question, string title, UIElement uiElement, string? defaultValue, PromptDialogConfig? config = null)
{
InitializeComponent();
MyTitleBar.Title = title;
@@ -36,11 +36,11 @@ public partial class PromptDialog
_config = config ?? new PromptDialogConfig();
DynamicContent.Content = uiElement;
if (DynamicContent.Content is TextBox textBox)
if (DynamicContent.Content is TextBox textBox && defaultValue != null)
{
textBox.Text = defaultValue;
}
else if (DynamicContent.Content is ComboBox comboBox)
else if (DynamicContent.Content is ComboBox comboBox && defaultValue != null)
{
comboBox.Text = defaultValue;
}
@@ -126,4 +126,4 @@ public partial class PromptDialog
{
Close();
}
}
}

View File

@@ -23,6 +23,7 @@ using System.Net.Http.Json;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using BetterGenshinImpact.GameTask.UseRedeemCode;
using BetterGenshinImpact.View.Windows;
using BetterGenshinImpact.ViewModel.Pages;
using DeviceId;
@@ -37,17 +38,15 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
private readonly IConfigService _configService;
public string Title => $"BetterGI · 更好的原神 · {Global.Version}{(RuntimeHelper.IsDebug ? " · Dev" : string.Empty)}";
[ObservableProperty]
private bool _isVisible = true;
[ObservableProperty] private bool _isVisible = true;
[ObservableProperty]
private WindowState _windowState = WindowState.Normal;
[ObservableProperty] private WindowState _windowState = WindowState.Normal;
[ObservableProperty]
private WindowBackdropType _currentBackdropType = WindowBackdropType.Auto;
[ObservableProperty] private WindowBackdropType _currentBackdropType = WindowBackdropType.Auto;
[ObservableProperty]
private bool _isWin11Later = OsVersionHelper.IsWindows11_OrGreater;
[ObservableProperty] private bool _isWin11Later = OsVersionHelper.IsWindows11_OrGreater;
private bool _firstActivated = true;
public AllConfig Config { get; set; }
@@ -61,7 +60,37 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
[RelayCommand]
private async Task OnActivated()
{
await ScriptRepoUpdater.Instance.ImportScriptFromClipboard();
// 首次激活时不处理
if (_firstActivated)
{
_firstActivated = false;
return;
}
// 激活时候获取剪切板内容 用于脚本导入、兑换码自动兑换等
try
{
if (Clipboard.ContainsText())
{
string clipboardText = Clipboard.GetText();
if (string.IsNullOrEmpty(clipboardText)
|| clipboardText.Length > 1000)
{
return;
}
// 1. 导入脚本
await ScriptRepoUpdater.Instance.ImportScriptFromClipboard(clipboardText);
// 2. 自动兑换码
await RedeemCodeManager.ImportFromClipboard(clipboardText);
}
}
catch
{
// 忽略异常,可能是因为没有权限访问剪切板
}
}
[RelayCommand]

View File

@@ -34,6 +34,7 @@ using BetterGenshinImpact.GameTask.AutoFight.Assets;
using BetterGenshinImpact.GameTask.Common.Map.Maps.Base;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.QuickTeleport.Assets;
using BetterGenshinImpact.GameTask.UseRedeemCode;
using BetterGenshinImpact.View;
using OpenCvSharp;
using Vanara.PInvoke;
@@ -581,8 +582,6 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel
Config.HotKeyConfig.Test1HotkeyType,
(_, _) =>
{
LowerHeadThenWalkToTask _lowerHeadThenWalkToTask = new("chest_tip.png", 20000);
_lowerHeadThenWalkToTask.Start(CancellationToken.None);
}
));
debugDirectory.Children.Add(new HotKeySettingModel(

View File

@@ -25,18 +25,21 @@ using BetterGenshinImpact.GameTask.Common.Element.Assets;
using BetterGenshinImpact.Helpers;
using Wpf.Ui;
using Wpf.Ui.Controls;
using Wpf.Ui.Violeta.Controls;
using BetterGenshinImpact.ViewModel.Pages.View;
using System.Linq;
using System.Reflection;
using System.Collections.Frozen;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using BetterGenshinImpact.GameTask.AutoArtifactSalvage;
using BetterGenshinImpact.GameTask.AutoStygianOnslaught;
using BetterGenshinImpact.View.Windows;
using BetterGenshinImpact.GameTask.GetGridIcons;
using BetterGenshinImpact.GameTask.Model.GameUI;
using BetterGenshinImpact.GameTask.UseRedeemCode;
using TextBox = Wpf.Ui.Controls.TextBox;
namespace BetterGenshinImpact.ViewModel.Pages;
@@ -169,6 +172,13 @@ public partial class TaskSettingsPageViewModel : ViewModel
.GetField(e.ToString())?
.GetCustomAttribute<DescriptionAttribute>()?
.Description ?? e.ToString());
[ObservableProperty]
private bool _switchAutoRedeemCodeEnabled;
[ObservableProperty]
private string _switchAutoRedeemCodeButtonText = "启动";
public TaskSettingsPageViewModel(IConfigService configService, INavigationService navigationService, TaskTriggerDispatcher taskTriggerDispatcher)
{
@@ -547,4 +557,44 @@ public partial class TaskSettingsPageViewModel : ViewModel
Process.Start("explorer.exe", path);
}
[RelayCommand]
private async Task OnSwitchAutoRedeemCode()
{
var multilineTextBox = new TextBox
{
TextWrapping = TextWrapping.Wrap,
AcceptsReturn = true,
Height = 340,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
PlaceholderText = "请在此输入兑换码,每行一条记录"
};
var p = new PromptDialog(
"输入兑换码",
"自动使用兑换码",
multilineTextBox,
null);
p.Height = 500;
p.ShowDialog();
if (p.DialogResult == true && !string.IsNullOrWhiteSpace(multilineTextBox.Text))
{
var codes = multilineTextBox.Text.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries)
.Select(code => code.Trim())
.Where(code => !string.IsNullOrEmpty(code))
.ToList();
if (codes.Count == 0)
{
Toast.Warning("没有有效的兑换码");
return;
}
SwitchAutoRedeemCodeEnabled = true;
await new TaskRunner()
.RunSoloTaskAsync(new UseRedemptionCodeTask(codes));
SwitchAutoRedeemCodeEnabled = false;
}
}
}