diff --git a/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropConfig.cs b/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropConfig.cs
index 88219585..79b3ec8f 100644
--- a/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropConfig.cs
+++ b/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropConfig.cs
@@ -1,11 +1,17 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
+using System.ComponentModel;
namespace BetterGenshinImpact.GameTask.AutoLeyLineOutcrop;
[Serializable]
public partial class AutoLeyLineOutcropConfig : ObservableObject
{
+ public AutoLeyLineOutcropConfig()
+ {
+ AttachFightConfigEvents(_fightConfig);
+ }
+
[ObservableProperty]
private string _leyLineOutcropType = "启示之花";
@@ -44,4 +50,31 @@ public partial class AutoLeyLineOutcropConfig : ObservableObject
[ObservableProperty]
private bool _isGoToSynthesizer = false;
+
+ [ObservableProperty]
+ private AutoLeyLineOutcropFightConfig _fightConfig = new();
+
+ partial void OnFightConfigChanged(AutoLeyLineOutcropFightConfig value)
+ {
+ AttachFightConfigEvents(value);
+ OnPropertyChanged(nameof(FightConfig));
+ }
+
+ private void AttachFightConfigEvents(AutoLeyLineOutcropFightConfig? config)
+ {
+ if (config == null)
+ {
+ return;
+ }
+
+ config.PropertyChanged -= OnFightConfigPropertyChanged;
+ config.PropertyChanged += OnFightConfigPropertyChanged;
+ config.FinishDetectConfig.PropertyChanged -= OnFightConfigPropertyChanged;
+ config.FinishDetectConfig.PropertyChanged += OnFightConfigPropertyChanged;
+ }
+
+ private void OnFightConfigPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ OnPropertyChanged(nameof(FightConfig));
+ }
}
diff --git a/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropFightConfig.cs b/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropFightConfig.cs
new file mode 100644
index 00000000..36cb4478
--- /dev/null
+++ b/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropFightConfig.cs
@@ -0,0 +1,116 @@
+using BetterGenshinImpact.GameTask.AutoFight;
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+
+namespace BetterGenshinImpact.GameTask.AutoLeyLineOutcrop;
+
+[Serializable]
+public partial class AutoLeyLineOutcropFightConfig : ObservableObject
+{
+ [ObservableProperty] private string _strategyName = "";
+
+ ///
+ /// 英文逗号分割,强制指定队伍角色。
+ ///
+ [ObservableProperty] private string _teamNames = "";
+
+ ///
+ /// 检测战斗结束。
+ ///
+ [ObservableProperty] private bool _fightFinishDetectEnabled = true;
+
+ ///
+ /// 根据技能CD优化出招人员。
+ ///
+ [ObservableProperty] private string _actionSchedulerByCd = "";
+
+ [Serializable]
+ public partial class FightFinishDetectConfig : ObservableObject
+ {
+ [ObservableProperty] private string _battleEndProgressBarColor = "";
+ [ObservableProperty] private string _battleEndProgressBarColorTolerance = "";
+ [ObservableProperty] private bool _fastCheckEnabled = false;
+ [ObservableProperty] private bool _rotateFindEnemyEnabled = false;
+ [ObservableProperty] private string _fastCheckParams = "";
+ [ObservableProperty] private string _checkEndDelay = "";
+ [ObservableProperty] private string _beforeDetectDelay = "";
+ [ObservableProperty] private int _rotaryFactor = 10;
+ [ObservableProperty] private bool _isFirstCheck = false;
+ [ObservableProperty] private bool _checkBeforeBurst = false;
+ }
+
+ [ObservableProperty] private FightFinishDetectConfig _finishDetectConfig = new();
+ [ObservableProperty] private string _guardianAvatar = string.Empty;
+ [ObservableProperty] private bool _guardianCombatSkip = false;
+ [ObservableProperty] private bool _guardianAvatarHold = false;
+ [ObservableProperty] private bool _burstEnabled = false;
+ [ObservableProperty] private bool _swimmingEnabled = false;
+ [ObservableProperty] private int _timeout = 120;
+
+ public void CopyFromAutoFightConfig(AutoFightConfig source)
+ {
+ StrategyName = source.StrategyName;
+ TeamNames = source.TeamNames;
+ FightFinishDetectEnabled = source.FightFinishDetectEnabled;
+ ActionSchedulerByCd = source.ActionSchedulerByCd;
+ GuardianAvatar = source.GuardianAvatar;
+ GuardianCombatSkip = source.GuardianCombatSkip;
+ GuardianAvatarHold = source.GuardianAvatarHold;
+ BurstEnabled = source.BurstEnabled;
+ SwimmingEnabled = source.SwimmingEnabled;
+ Timeout = source.Timeout;
+
+ FinishDetectConfig.BattleEndProgressBarColor = source.FinishDetectConfig.BattleEndProgressBarColor;
+ FinishDetectConfig.BattleEndProgressBarColorTolerance = source.FinishDetectConfig.BattleEndProgressBarColorTolerance;
+ FinishDetectConfig.FastCheckEnabled = source.FinishDetectConfig.FastCheckEnabled;
+ FinishDetectConfig.RotateFindEnemyEnabled = source.FinishDetectConfig.RotateFindEnemyEnabled;
+ FinishDetectConfig.FastCheckParams = source.FinishDetectConfig.FastCheckParams;
+ FinishDetectConfig.CheckEndDelay = source.FinishDetectConfig.CheckEndDelay;
+ FinishDetectConfig.BeforeDetectDelay = source.FinishDetectConfig.BeforeDetectDelay;
+ FinishDetectConfig.RotaryFactor = source.FinishDetectConfig.RotaryFactor;
+ FinishDetectConfig.IsFirstCheck = source.FinishDetectConfig.IsFirstCheck;
+ FinishDetectConfig.CheckBeforeBurst = source.FinishDetectConfig.CheckBeforeBurst;
+ }
+
+ public AutoFightConfig ToAutoFightConfig()
+ {
+ var config = new AutoFightConfig
+ {
+ StrategyName = StrategyName,
+ TeamNames = TeamNames,
+ FightFinishDetectEnabled = FightFinishDetectEnabled,
+ ActionSchedulerByCd = ActionSchedulerByCd,
+ GuardianAvatar = GuardianAvatar,
+ GuardianCombatSkip = GuardianCombatSkip,
+ GuardianAvatarHold = GuardianAvatarHold,
+ BurstEnabled = BurstEnabled,
+ SwimmingEnabled = SwimmingEnabled,
+ Timeout = Timeout,
+
+ // 地脉花战斗不启用战斗后拾取逻辑。
+ PickDropsAfterFightEnabled = false,
+ PickDropsAfterFightSeconds = 0,
+ KazuhaPickupEnabled = false,
+ QinDoublePickUp = false,
+ OnlyPickEliteDropsMode = "DisableAutoPickupForNonElite",
+ KazuhaPartyName = string.Empty,
+ BattleThresholdForLoot = null
+ };
+
+ config.FinishDetectConfig = new AutoFightConfig.FightFinishDetectConfig
+ {
+ BattleEndProgressBarColor = FinishDetectConfig.BattleEndProgressBarColor,
+ BattleEndProgressBarColorTolerance = FinishDetectConfig.BattleEndProgressBarColorTolerance,
+ FastCheckEnabled = FinishDetectConfig.FastCheckEnabled,
+ RotateFindEnemyEnabled = FinishDetectConfig.RotateFindEnemyEnabled,
+ FastCheckParams = FinishDetectConfig.FastCheckParams,
+ CheckEndDelay = FinishDetectConfig.CheckEndDelay,
+ BeforeDetectDelay = FinishDetectConfig.BeforeDetectDelay,
+ RotaryFactor = FinishDetectConfig.RotaryFactor,
+ IsFirstCheck = FinishDetectConfig.IsFirstCheck,
+ CheckBeforeBurst = FinishDetectConfig.CheckBeforeBurst
+ };
+
+ return config;
+ }
+}
diff --git a/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropParam.cs b/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropParam.cs
index 8f728989..527282d6 100644
--- a/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropParam.cs
+++ b/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropParam.cs
@@ -23,6 +23,8 @@ public class AutoLeyLineOutcropParam:BaseTaskParam
public string Team { get; set; }
//战斗超时时间
public int Timeout { get; set; }
+ //地脉花独立战斗配置
+ public AutoLeyLineOutcropFightConfig FightConfig { get; set; } = new();
//是否前往合成台合成浓缩树脂
public bool IsGoToSynthesizer { get; set; }
//是否使用脆弱树脂
@@ -46,6 +48,7 @@ public class AutoLeyLineOutcropParam:BaseTaskParam
FriendshipTeam= config.FriendshipTeam;
Team= config.Team;
Timeout= config.Timeout;
+ FightConfig = config.FightConfig ?? new AutoLeyLineOutcropFightConfig();
IsGoToSynthesizer=config.IsGoToSynthesizer;
UseFragileResin= config.UseFragileResin;
UseTransientResin= config.UseTransientResin;
@@ -67,4 +70,4 @@ public class AutoLeyLineOutcropParam:BaseTaskParam
this.Country = country;
this.LeyLineOutcropType = leyLineOutcropType;
}
-}
\ No newline at end of file
+}
diff --git a/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropTask.cs b/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropTask.cs
index ed8e5a16..ab73f075 100644
--- a/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropTask.cs
+++ b/BetterGenshinImpact/GameTask/AutoLeyLineOutcrop/AutoLeyLineOutcropTask.cs
@@ -17,6 +17,7 @@ using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Service.Notification;
+using BetterGenshinImpact.View.Drawable;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using System;
@@ -31,6 +32,7 @@ using System.Threading.Tasks;
using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception;
using Vanara.PInvoke;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
+using BetterGenshinImpact.View;
namespace BetterGenshinImpact.GameTask.AutoLeyLineOutcrop;
@@ -70,6 +72,13 @@ public class AutoLeyLineOutcropTask : ISoloTask
private const int MaxRecheckCount = 3;
private const int MaxConsecutiveFailures = 5;
+ private const string OcrFlowOverlayKey = "AutoLeyLineOutcrop.OcrFlow";
+ private const string OcrFightOverlayKey = "AutoLeyLineOutcrop.OcrFight";
+ private const int OcrOverlayRenderLeadMs = 300;
+ private static readonly System.Drawing.Pen OcrOverlayPen = new(System.Drawing.Color.Lime, 2);
+ private bool _overlayDisplayTemporarilyEnabled;
+ private bool _overlayDisplayOriginalValue;
+ private DateTime _lastMaskBringTopTime = DateTime.MinValue;
public string Name => "自动地脉花";
@@ -86,6 +95,7 @@ public class AutoLeyLineOutcropTask : ISoloTask
try
{
Initialize();
+ EnsureMaskOverlayVisible();
var runTimesValue = await HandleResinExhaustionMode();
if (runTimesValue <= 0)
{
@@ -119,16 +129,24 @@ public class AutoLeyLineOutcropTask : ISoloTask
{
try
{
- await EnsureExitRewardPage();
- }
- catch (Exception ex)
- {
- _logger.LogDebug(ex, "地脉花结束后尝试退出奖励界面失败");
- }
+ try
+ {
+ await EnsureExitRewardPage();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogDebug(ex, "地脉花结束后尝试退出奖励界面失败");
+ }
- if (!_marksStatus)
+ if (!_marksStatus)
+ {
+ await OpenCustomMarks();
+ }
+ }
+ finally
{
- await OpenCustomMarks();
+ ClearOcrOverlayKeys();
+ RestoreMaskOverlayVisible();
}
}
}
@@ -144,6 +162,8 @@ public class AutoLeyLineOutcropTask : ISoloTask
private void ValidateSettings()
{
+ _taskParam.FightConfig ??= new AutoLeyLineOutcropFightConfig();
+
if (string.IsNullOrWhiteSpace(_taskParam.LeyLineOutcropType))
{
throw new Exception("地脉花类型未选择");
@@ -168,6 +188,20 @@ public class AutoLeyLineOutcropTask : ISoloTask
{
_taskParam.Count = 1;
}
+
+ if (string.IsNullOrWhiteSpace(_taskParam.FightConfig.StrategyName) && _taskParam.Timeout > 0)
+ {
+ _taskParam.FightConfig.Timeout = _taskParam.Timeout;
+ }
+
+ if (_taskParam.FightConfig.Timeout <= 0)
+ {
+ _taskParam.FightConfig.Timeout = _taskParam.Timeout > 0 ? _taskParam.Timeout : 120;
+ }
+ else
+ {
+ _taskParam.Timeout = _taskParam.FightConfig.Timeout;
+ }
}
private void LoadConfigData()
@@ -635,7 +669,7 @@ public class AutoLeyLineOutcropTask : ISoloTask
var lastRoute = path.Routes.Last();
var targetRoute = lastRoute.Replace("Assets/pathing/", "Assets/pathing/target/").Replace("-rerun", "");
- await ProcessLeyLineOutcrop(_taskParam.Timeout, targetRoute);
+ await ProcessLeyLineOutcrop(_taskParam.FightConfig.Timeout, targetRoute);
var rewardSuccess = await AttemptReward();
if (!rewardSuccess)
@@ -819,18 +853,24 @@ public class AutoLeyLineOutcropTask : ISoloTask
await Delay(500, _ct);
_logger.LogDebug("检测地脉花交互状态,重试次数: {Retries}/{MaxRetries}", retries + 1, maxRetries);
using var capture = CaptureToRectArea();
+ using var ocrOverlayScope = DrawOcrOverlayScope(capture, OcrFlowOverlayKey, _ocrRo2!.RegionOfInterest, _ocrRo3!.RegionOfInterest);
+ await WaitOcrOverlayRenderTick();
+ string result1Text;
+ string result2Text;
var result1 = FindSafe(capture, _ocrRo2!);
var result2 = FindSafe(capture, _ocrRo3!);
- _logger.LogDebug("OCR结果: result1='{Text1}', result2='{Text2}'", result1.Text, result2.Text);
+ result1Text = result1.Text;
+ result2Text = result2.Text;
+ _logger.LogDebug("OCR结果: result1='{Text1}', result2='{Text2}'", result1Text, result2Text);
- if (result2.Text.Contains("之花", StringComparison.Ordinal))
+ if (result2Text.Contains("之花", StringComparison.Ordinal))
{
_logger.LogDebug("识别到地脉之花入口");
await SwitchToFriendshipTeamIfNeeded();
return true;
}
- if (result2.Text.Contains("溢口", StringComparison.Ordinal))
+ if (result2Text.Contains("溢口", StringComparison.Ordinal))
{
_logger.LogDebug("识别到溢口提示,尝试交互");
Simulation.SendInput.SimulateAction(GIActions.PickUpOrInteract);
@@ -838,7 +878,7 @@ public class AutoLeyLineOutcropTask : ISoloTask
Simulation.SendInput.SimulateAction(GIActions.PickUpOrInteract);
await Delay(500, _ct);
}
- else if (!ContainsFightText(result1.Text))
+ else if (!ContainsFightText(result1Text))
{
_logger.LogDebug("未识别到战斗提示,执行路径: {Path}", targetPath);
await RunPathingFile(targetPath);
@@ -889,6 +929,7 @@ public class AutoLeyLineOutcropTask : ISoloTask
private async Task AutoFight(int timeoutSeconds)
{
var fightCts = CancellationTokenSource.CreateLinkedTokenSource(_ct);
+ using var autoFightConfigScope = UseLeyLineAutoFightConfigScope();
// Ley line uses OCR-based finish detection; disable auto-fight finish detect.
var fightTask = StartAutoFightWithoutFinishDetect(fightCts.Token);
var fightResult = await RecognizeTextInRegion(timeoutSeconds * 1000);
@@ -915,7 +956,7 @@ public class AutoLeyLineOutcropTask : ISoloTask
private Task StartAutoFightWithoutFinishDetect(CancellationToken ct)
{
- var autoFightConfig = TaskContext.Instance().Config.AutoFightConfig;
+ var autoFightConfig = BuildLeyLineAutoFightConfig();
var strategyPath = BuildAutoFightStrategyPath(autoFightConfig);
var taskParam = new AutoFightParam(strategyPath, autoFightConfig)
{
@@ -925,9 +966,39 @@ public class AutoLeyLineOutcropTask : ISoloTask
// Avoid false finish signals for ley line fights.
taskParam.FinishDetectConfig.FastCheckEnabled = false;
taskParam.FinishDetectConfig.RotateFindEnemyEnabled = false;
+ taskParam.PickDropsAfterFightEnabled = false;
+ taskParam.KazuhaPickupEnabled = false;
+ taskParam.QinDoublePickUp = false;
+ taskParam.OnlyPickEliteDropsMode = "DisableAutoPickupForNonElite";
return new AutoFightTask(taskParam).Start(ct);
}
+ private IDisposable UseLeyLineAutoFightConfigScope()
+ {
+ var allConfig = TaskContext.Instance().Config;
+ var original = allConfig.AutoFightConfig;
+ allConfig.AutoFightConfig = BuildLeyLineAutoFightConfig();
+ return new AutoFightConfigScope(allConfig, original);
+ }
+
+ private AutoFightConfig BuildLeyLineAutoFightConfig()
+ {
+ var globalAutoFightConfig = TaskContext.Instance().Config.AutoFightConfig;
+ var fightConfig = _taskParam.FightConfig;
+ if (string.IsNullOrWhiteSpace(fightConfig.StrategyName))
+ {
+ fightConfig.CopyFromAutoFightConfig(globalAutoFightConfig);
+ }
+
+ if (fightConfig.Timeout <= 0)
+ {
+ fightConfig.Timeout = _taskParam.Timeout > 0 ? _taskParam.Timeout : Math.Max(globalAutoFightConfig.Timeout, 1);
+ }
+
+ _taskParam.Timeout = fightConfig.Timeout;
+ return fightConfig.ToAutoFightConfig();
+ }
+
private static string BuildAutoFightStrategyPath(AutoFightConfig config)
{
var path = Global.Absolute(@"User\AutoFight\" + config.StrategyName + ".txt");
@@ -954,8 +1025,13 @@ public class AutoLeyLineOutcropTask : ISoloTask
while ((DateTime.UtcNow - start).TotalMilliseconds < timeoutMs)
{
using var capture = CaptureToRectArea();
+ using var ocrOverlayScope = DrawOcrOverlayScope(capture, OcrFightOverlayKey, _ocrRo1!.RegionOfInterest, _ocrRo2!.RegionOfInterest);
+ await WaitOcrOverlayRenderTick();
+ string text;
+ bool foundText;
var result = capture.Find(_ocrRo1!);
- var text = result.Text;
+ text = result.Text;
+ foundText = RecognizeFightText(capture);
if (successKeywords.Any(text.Contains))
{
@@ -969,7 +1045,6 @@ public class AutoLeyLineOutcropTask : ISoloTask
return false;
}
- var foundText = RecognizeFightText(capture);
if (!foundText)
{
noTextCount++;
@@ -1278,6 +1353,110 @@ public class AutoLeyLineOutcropTask : ISoloTask
return false;
}
+ private IDisposable DrawOcrOverlayScope(ImageRegion capture, string key, params Rect[] rois)
+ {
+ var drawList = new List(rois.Length);
+ foreach (var roi in rois)
+ {
+ var clamped = roi.ClampTo(capture.Width, capture.Height);
+ if (clamped.Width <= 0 || clamped.Height <= 0)
+ {
+ continue;
+ }
+
+ drawList.Add(capture.ToRectDrawable(clamped, key, OcrOverlayPen));
+ }
+
+ var drawContent = VisionContext.Instance().DrawContent;
+ drawContent.PutOrRemoveRectList(key, drawList.Count > 0 ? drawList : null);
+ RefreshMaskWindowForOverlay();
+ return new OcrOverlayScope(drawContent, key);
+ }
+
+ private void ClearOcrOverlayKeys()
+ {
+ var drawContent = VisionContext.Instance().DrawContent;
+ drawContent.RemoveRect(OcrFlowOverlayKey);
+ drawContent.RemoveRect(OcrFightOverlayKey);
+ drawContent.PutOrRemoveTextList(OcrFlowOverlayKey, null);
+ drawContent.PutOrRemoveTextList(OcrFightOverlayKey, null);
+ }
+
+ private async Task WaitOcrOverlayRenderTick()
+ {
+ await Task.Yield();
+ await Task.Delay(OcrOverlayRenderLeadMs, _ct);
+ }
+
+ private void EnsureMaskOverlayVisible()
+ {
+ var config = TaskContext.Instance().Config.MaskWindowConfig;
+ _overlayDisplayOriginalValue = config.DisplayRecognitionResultsOnMask;
+ if (!config.DisplayRecognitionResultsOnMask)
+ {
+ config.DisplayRecognitionResultsOnMask = true;
+ _overlayDisplayTemporarilyEnabled = true;
+ }
+
+ var maskWindow = MaskWindow.InstanceNullable();
+ if (maskWindow != null)
+ {
+ maskWindow.Invoke(() =>
+ {
+ maskWindow.Topmost = true;
+ if (!maskWindow.IsVisible)
+ {
+ maskWindow.Show();
+ }
+
+ maskWindow.BringToTop();
+ });
+ }
+ }
+
+ private void RestoreMaskOverlayVisible()
+ {
+ if (!_overlayDisplayTemporarilyEnabled)
+ {
+ return;
+ }
+
+ TaskContext.Instance().Config.MaskWindowConfig.DisplayRecognitionResultsOnMask = _overlayDisplayOriginalValue;
+ _overlayDisplayTemporarilyEnabled = false;
+ }
+
+ private void RefreshMaskWindowForOverlay()
+ {
+ var maskWindow = MaskWindow.InstanceNullable();
+ if (maskWindow == null)
+ {
+ return;
+ }
+
+ var now = DateTime.UtcNow;
+ var shouldBringTop = now - _lastMaskBringTopTime > TimeSpan.FromSeconds(1);
+ if (shouldBringTop)
+ {
+ _lastMaskBringTopTime = now;
+ }
+
+ maskWindow.Invoke(() =>
+ {
+ maskWindow.Topmost = true;
+ if (!maskWindow.IsVisible)
+ {
+ maskWindow.Show();
+ }
+
+ if (shouldBringTop)
+ {
+ maskWindow.BringToTop();
+ }
+
+ maskWindow.Refresh();
+ });
+ }
+
private async Task CheckOriginalResinEmpty()
{
using var capture = CaptureToRectArea();
@@ -1497,12 +1676,22 @@ public class AutoLeyLineOutcropTask : ISoloTask
await Delay(1000, _ct);
await FindAndClickCountry(country);
- await FindAndCancelTrackingInBook();
+ var trackButton = await FindAndCancelTrackingInBook();
for (var retry = 0; retry < 3; retry++)
{
await Delay(1000, _ct);
- GameCaptureRegion.GameRegion1080PPosClick(1500, 850);
+ if (trackButton != null)
+ {
+ trackButton.Click();
+ _logger.LogDebug("通过 OCR 结果点击追踪按钮,text={Text}", trackButton.Text);
+ }
+ else
+ {
+ GameCaptureRegion.GameRegion1080PPosClick(1500, 850);
+ _logger.LogDebug("未识别到追踪按钮,回退固定坐标点击");
+ }
+
await Delay(2500, _ct);
if (await CheckBigMapOpened())
@@ -1514,7 +1703,7 @@ public class AutoLeyLineOutcropTask : ISoloTask
{
await _returnMainUiTask.Start(_ct);
await FindAndClickCountry(country);
- await FindAndCancelTrackingInBook();
+ trackButton = await FindAndCancelTrackingInBook();
}
else
{
@@ -1554,13 +1743,29 @@ public class AutoLeyLineOutcropTask : ISoloTask
target.Click();
}
- private async Task FindAndCancelTrackingInBook()
+ private static bool IsTrackButtonText(string text)
+ {
+ return (text.Contains("追踪", StringComparison.Ordinal) || text.Contains("追蹤", StringComparison.Ordinal))
+ && !text.Contains("停止", StringComparison.Ordinal);
+ }
+
+ private async Task FindAndCancelTrackingInBook()
{
using var capture = CaptureToRectArea();
var list = capture.FindMulti(_ocrRoThis);
+ var track = list.FirstOrDefault(r => IsTrackButtonText(r.Text));
var stop = list.FirstOrDefault(r => r.Text.Contains("停止", StringComparison.Ordinal));
- stop?.Click();
- await Delay(1000, _ct);
+ if (stop != null)
+ {
+ stop.Click();
+ await Delay(1000, _ct);
+
+ using var refreshCapture = CaptureToRectArea();
+ var refreshList = refreshCapture.FindMulti(_ocrRoThis);
+ track = refreshList.FirstOrDefault(r => IsTrackButtonText(r.Text));
+ }
+
+ return track;
}
private async Task CancelTrackingInMap()
@@ -1940,6 +2145,39 @@ public class AutoLeyLineOutcropTask : ISoloTask
public int FragileResinTimes { get; set; }
}
+ private sealed class OcrOverlayScope(DrawContent drawContent, string key) : IDisposable
+ {
+ private bool _disposed;
+
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+ drawContent.RemoveRect(key);
+ drawContent.PutOrRemoveTextList(key, null);
+ }
+ }
+
+ private sealed class AutoFightConfigScope(AllConfig allConfig, AutoFightConfig originalConfig) : IDisposable
+ {
+ private bool _disposed;
+
+ public void Dispose()
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ _disposed = true;
+ allConfig.AutoFightConfig = originalConfig;
+ }
+ }
+
private readonly struct UseButton
{
public int X { get; }
@@ -1958,4 +2196,4 @@ public class AutoLeyLineOutcropTask : ISoloTask
GameCaptureRegion.GameRegion1080PPosClick(X, Y);
}
}
-}
\ No newline at end of file
+}
diff --git a/BetterGenshinImpact/View/MaskWindow.xaml.cs b/BetterGenshinImpact/View/MaskWindow.xaml.cs
index 9794db2e..7e0dba17 100644
--- a/BetterGenshinImpact/View/MaskWindow.xaml.cs
+++ b/BetterGenshinImpact/View/MaskWindow.xaml.cs
@@ -52,7 +52,6 @@ public partial class MaskWindow : Window
private MaskWindowConfig? _maskWindowConfig;
private MapLabelSearchWindow? _mapLabelSearchWindow;
private CancellationTokenSource? _mapLabelCategorySelectCts;
-
static MaskWindow()
{
if (Application.Current.TryFindResource("TextThemeFontFamily") is FontFamily fontFamily)
@@ -463,96 +462,98 @@ public partial class MaskWindow : Window
return;
}
- // 先有上方判断的原因是,有可能Render的时候,配置还未初始化
- if (!TaskContext.Instance().Config.MaskWindowConfig.DisplayRecognitionResultsOnMask)
+ var displayRecognitionResults = TaskContext.Instance().Config.MaskWindowConfig.DisplayRecognitionResultsOnMask;
+ if (!displayRecognitionResults)
{
return;
}
- foreach (var kv in VisionContext.Instance().DrawContent.RectList)
+ if (displayRecognitionResults)
{
- foreach (var drawable in kv.Value)
+ foreach (var kv in VisionContext.Instance().DrawContent.RectList)
{
- if (!drawable.IsEmpty)
+ foreach (var drawable in kv.Value)
{
- drawingContext.DrawRectangle(Brushes.Transparent,
- new Pen(new SolidColorBrush(drawable.Pen.Color.ToWindowsColor()), drawable.Pen.Width),
- drawable.Rect);
+ if (!drawable.IsEmpty)
+ {
+ drawingContext.DrawRectangle(
+ Brushes.Transparent,
+ new Pen(new SolidColorBrush(drawable.Pen.Color.ToWindowsColor()), drawable.Pen.Width),
+ drawable.Rect);
+ }
}
}
- }
- foreach (var kv in VisionContext.Instance().DrawContent.LineList)
- {
- foreach (var drawable in kv.Value)
+ foreach (var kv in VisionContext.Instance().DrawContent.LineList)
{
- drawingContext.DrawLine(new Pen(new SolidColorBrush(drawable.Pen.Color.ToWindowsColor()), drawable.Pen.Width), drawable.P1, drawable.P2);
- }
- }
-
- foreach (var kv in VisionContext.Instance().DrawContent.TextList)
- {
- bool isSkillCd = kv.Key == "SkillCdText";
- var systemInfo = TaskContext.Instance().SystemInfo;
- // 使用不封顶的物理比例进行 UI 大小缩放
- var scaleTo1080 = systemInfo.ScaleTo1080PRatio;
-
- foreach (var drawable in kv.Value)
- {
- if (!drawable.IsEmpty)
+ foreach (var drawable in kv.Value)
{
- var pixelsPerDip = VisualTreeHelper.GetDpi(this).PixelsPerDip;
- var renderPoint = new Point(drawable.Point.X / pixelsPerDip, drawable.Point.Y / pixelsPerDip);
+ drawingContext.DrawLine(new Pen(new SolidColorBrush(drawable.Pen.Color.ToWindowsColor()), drawable.Pen.Width), drawable.P1, drawable.P2);
+ }
+ }
- if (isSkillCd)
+ foreach (var kv in VisionContext.Instance().DrawContent.TextList)
+ {
+ bool isSkillCd = kv.Key == "SkillCdText";
+ var systemInfo = TaskContext.Instance().SystemInfo;
+ var scaleTo1080 = systemInfo.ScaleTo1080PRatio;
+
+ foreach (var drawable in kv.Value)
+ {
+ if (!drawable.IsEmpty)
{
- // 自定义缩放
- var skillConfigScale = TaskContext.Instance().Config.SkillCdConfig.Scale;
- double scaledFontSize = (26 * scaleTo1080 * skillConfigScale) / pixelsPerDip;
- var mediumTypeface = new Typeface(_fgiTypeface.FontFamily, _fgiTypeface.Style, FontWeights.Medium, _fgiTypeface.Stretch);
- bool isZeroCd =
- double.TryParse(drawable.Text, NumberStyles.Float, CultureInfo.InvariantCulture, out var cdValue)
- && Math.Abs(cdValue) < 0.8;
+ var pixelsPerDip = VisualTreeHelper.GetDpi(this).PixelsPerDip;
+ var renderPoint = new Point(drawable.Point.X / pixelsPerDip, drawable.Point.Y / pixelsPerDip);
- // 从配置读取颜色
- var skillConfig = TaskContext.Instance().Config.SkillCdConfig;
- string textColorStr = isZeroCd ? skillConfig.TextReadyColor : skillConfig.TextNormalColor;
- string bgColorStr = isZeroCd ? skillConfig.BackgroundReadyColor : skillConfig.BackgroundNormalColor;
+ if (isSkillCd)
+ {
+ var skillConfigScale = TaskContext.Instance().Config.SkillCdConfig.Scale;
+ double scaledFontSize = (26 * scaleTo1080 * skillConfigScale) / pixelsPerDip;
+ var mediumTypeface = new Typeface(_fgiTypeface.FontFamily, _fgiTypeface.Style, FontWeights.Medium, _fgiTypeface.Stretch);
+ bool isZeroCd =
+ double.TryParse(drawable.Text, NumberStyles.Float, CultureInfo.InvariantCulture, out var cdValue)
+ && Math.Abs(cdValue) < 0.8;
- Color textColor = ParseColor(textColorStr) ?? (isZeroCd ? Color.FromRgb(93, 204, 23) : Color.FromRgb(218, 74, 35));
- Color bgColor = ParseColor(bgColorStr) ?? Colors.White;
+ var skillConfig = TaskContext.Instance().Config.SkillCdConfig;
+ string textColorStr = isZeroCd ? skillConfig.TextReadyColor : skillConfig.TextNormalColor;
+ string bgColorStr = isZeroCd ? skillConfig.BackgroundReadyColor : skillConfig.BackgroundNormalColor;
- Brush textBrush = new SolidColorBrush(textColor);
- Brush bgBrush = new SolidColorBrush(bgColor);
+ Color textColor = ParseColor(textColorStr) ?? (isZeroCd ? Color.FromRgb(93, 204, 23) : Color.FromRgb(218, 74, 35));
+ Color bgColor = ParseColor(bgColorStr) ?? Colors.White;
- var formattedText = new FormattedText(
- drawable.Text,
- CultureInfo.GetCultureInfo("zh-cn"),
- FlowDirection.LeftToRight,
- mediumTypeface,
- scaledFontSize,
- textBrush,
- pixelsPerDip);
+ Brush textBrush = new SolidColorBrush(textColor);
+ Brush bgBrush = new SolidColorBrush(bgColor);
- double px = (6 * scaleTo1080 * skillConfigScale) / pixelsPerDip;
- double py = (2 * scaleTo1080 * skillConfigScale) / pixelsPerDip;
- double radius = (5 * scaleTo1080 * skillConfigScale) / pixelsPerDip;
- var bgRect = new Rect(renderPoint.X - px, renderPoint.Y - py, formattedText.Width + px * 2, formattedText.Height + py * 2);
- drawingContext.DrawRoundedRectangle(bgBrush, null, bgRect, radius, radius);
- drawingContext.DrawText(formattedText, renderPoint);
- }
- else
- {
- double defaultFontSize = (36 * scaleTo1080) / pixelsPerDip;
- drawingContext.DrawText(new FormattedText(drawable.Text,
- CultureInfo.GetCultureInfo("zh-cn"),
- FlowDirection.LeftToRight,
- _typeface,
- defaultFontSize, Brushes.Black, pixelsPerDip), renderPoint);
+ var formattedText = new FormattedText(
+ drawable.Text,
+ CultureInfo.GetCultureInfo("zh-cn"),
+ FlowDirection.LeftToRight,
+ mediumTypeface,
+ scaledFontSize,
+ textBrush,
+ pixelsPerDip);
+
+ double px = (6 * scaleTo1080 * skillConfigScale) / pixelsPerDip;
+ double py = (2 * scaleTo1080 * skillConfigScale) / pixelsPerDip;
+ double radius = (5 * scaleTo1080 * skillConfigScale) / pixelsPerDip;
+ var bgRect = new Rect(renderPoint.X - px, renderPoint.Y - py, formattedText.Width + px * 2, formattedText.Height + py * 2);
+ drawingContext.DrawRoundedRectangle(bgBrush, null, bgRect, radius, radius);
+ drawingContext.DrawText(formattedText, renderPoint);
+ }
+ else
+ {
+ double defaultFontSize = (36 * scaleTo1080) / pixelsPerDip;
+ drawingContext.DrawText(new FormattedText(drawable.Text,
+ CultureInfo.GetCultureInfo("zh-cn"),
+ FlowDirection.LeftToRight,
+ _typeface,
+ defaultFontSize, Brushes.Black, pixelsPerDip), renderPoint);
+ }
}
}
}
}
+
}
catch (Exception e)
{
diff --git a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml
index 88407f4f..54318dc2 100644
--- a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml
+++ b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml
@@ -2476,6 +2476,83 @@
ItemsSource="{Binding Source={x:Static pages:TaskSettingsPageViewModel.LeyLineOutcropCountryList}}"
SelectedItem="{Binding Config.AutoLeyLineOutcropConfig.Country, Mode=TwoWay}" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2696,7 +2773,7 @@
Maximum="9999"
Minimum="1"
ValidationMode="InvalidInputOverwritten"
- Value="{Binding Config.AutoLeyLineOutcropConfig.Timeout, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
+ Value="{Binding Config.AutoLeyLineOutcropConfig.FightConfig.Timeout, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />