diff --git a/BetterGenshinImpact/Core/Simulator/PostMessageSimulator.cs b/BetterGenshinImpact/Core/Simulator/PostMessageSimulator.cs index 881d3aea..1089fde2 100644 --- a/BetterGenshinImpact/Core/Simulator/PostMessageSimulator.cs +++ b/BetterGenshinImpact/Core/Simulator/PostMessageSimulator.cs @@ -65,6 +65,16 @@ public class PostMessageSimulator return this; } + public PostMessageSimulator LeftButtonClickBackground() + { + User32.PostMessage(_hWnd, User32.WindowMessage.WM_ACTIVATE, 1, 0); + IntPtr p = (16 << 16) | 16; + User32.PostMessage(_hWnd, WM_LBUTTONDOWN, IntPtr.Zero, p); + Thread.Sleep(100); + User32.PostMessage(_hWnd, WM_LBUTTONUP, IntPtr.Zero, p); + return this; + } + /// /// 默认位置左键按下 /// diff --git a/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipConfig.cs b/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipConfig.cs index 73aaefbe..0580ebf7 100644 --- a/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipConfig.cs +++ b/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipConfig.cs @@ -1,78 +1,82 @@ using CommunityToolkit.Mvvm.ComponentModel; using System; -namespace BetterGenshinImpact.GameTask.AutoSkip +namespace BetterGenshinImpact.GameTask.AutoSkip; + +/// +/// 自动跳过剧情配置 +/// +[Serializable] +public partial class AutoSkipConfig : ObservableObject { /// - /// 自动跳过剧情配置 + /// 触发器是否启用 + /// 启用后: + /// 1. 快速跳过对话 + /// 2. 自动点击一个识别到的选项 + /// 3. 黑屏过长自动点击跳过 /// - [Serializable] - public partial class AutoSkipConfig : ObservableObject + [ObservableProperty] private bool _enabled = true; + + /// + /// 快速跳过对话 + /// + [ObservableProperty] private bool _quicklySkipConversationsEnabled = true; + + public int ChatOptionTextWidth { get; set; } = 280; + + public int ExpeditionOptionTextWidth { get; set; } = 130; + + /// + /// 选择选项前的延迟(毫秒) + /// + [ObservableProperty] private int _afterChooseOptionSleepDelay = 0; + + /// + /// 自动领取每日委托奖励 + /// + [ObservableProperty] private bool _autoGetDailyRewardsEnabled = true; + + /// + /// 自动重新派遣 + /// + [ObservableProperty] private bool _autoReExploreEnabled = true; + + /// + /// 自动重新派遣使用角色配置,逗号分割 + /// + [Obsolete] + [ObservableProperty] private string _autoReExploreCharacter = ""; + + /// + /// 优先选择第一个选项 + /// 优先选择最后一个选项 + /// 不选择选项 + /// + [ObservableProperty] private string _clickChatOption = "优先选择最后一个选项"; + + /// + /// 自动邀约启用 + /// + [ObservableProperty] private bool _autoHangoutEventEnabled = false; + + /// + /// 自动邀约启用 + /// + [ObservableProperty] private string _autoHangoutEndChoose = string.Empty; + + public bool IsClickFirstChatOption() { - /// - /// 触发器是否启用 - /// 启用后: - /// 1. 快速跳过对话 - /// 2. 自动点击一个识别到的选项 - /// 3. 黑屏过长自动点击跳过 - /// - [ObservableProperty] private bool _enabled = true; - - /// - /// 快速跳过对话 - /// - [ObservableProperty] private bool _quicklySkipConversationsEnabled = true; - - public int ChatOptionTextWidth { get; set; } = 280; - - public int ExpeditionOptionTextWidth { get; set; } = 130; - - /// - /// 选择选项前的延迟(毫秒) - /// - [ObservableProperty] private int _afterChooseOptionSleepDelay = 0; - - /// - /// 自动领取每日委托奖励 - /// - [ObservableProperty] private bool _autoGetDailyRewardsEnabled = true; - - /// - /// 自动重新派遣 - /// - [ObservableProperty] private bool _autoReExploreEnabled = true; - - /// - /// 自动重新派遣使用角色配置,逗号分割 - /// - [Obsolete] - [ObservableProperty] private string _autoReExploreCharacter = ""; - - /// - /// 优先选择第一个选项 - /// 优先选择最后一个选项 - /// 不选择选项 - /// - [ObservableProperty] private string _clickChatOption = "优先选择最后一个选项"; - - /// - /// 自动邀约启用 - /// - [ObservableProperty] private bool _autoHangoutEventEnabled = false; - - /// - /// 自动邀约启用 - /// - [ObservableProperty] private string _autoHangoutEndChoose = string.Empty; - - public bool IsClickFirstChatOption() - { - return ClickChatOption == "优先选择第一个选项"; - } - - public bool IsClickNoneChatOption() - { - return ClickChatOption == "不选择选项"; - } + return ClickChatOption == "优先选择第一个选项"; } + + public bool IsClickNoneChatOption() + { + return ClickChatOption == "不选择选项"; + } + + /// + /// 后台运行 + /// + [ObservableProperty] private bool _runBackgroundEnabled = false; } diff --git a/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipTrigger.cs b/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipTrigger.cs index 814178e9..c099c9c9 100644 --- a/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipTrigger.cs +++ b/BetterGenshinImpact/GameTask/AutoSkip/AutoSkipTrigger.cs @@ -1,4 +1,5 @@ using BetterGenshinImpact.Core.Config; +using BetterGenshinImpact.Core.Recognition; using BetterGenshinImpact.Core.Recognition.OCR; using BetterGenshinImpact.Core.Recognition.OpenCv; using BetterGenshinImpact.Core.Simulator; @@ -6,12 +7,12 @@ using BetterGenshinImpact.GameTask.AutoSkip.Assets; using BetterGenshinImpact.GameTask.AutoSkip.Model; using BetterGenshinImpact.GameTask.Common; using BetterGenshinImpact.GameTask.Common.Element.Assets; +using BetterGenshinImpact.GameTask.Model.Area; using BetterGenshinImpact.Helpers; using BetterGenshinImpact.Service; using BetterGenshinImpact.View.Drawable; using Microsoft.Extensions.Logging; using OpenCvSharp; -using Sdcb.PaddleOCR; using System; using System.Collections.Generic; using System.Diagnostics; @@ -20,8 +21,6 @@ using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; -using BetterGenshinImpact.Core.Recognition; -using BetterGenshinImpact.GameTask.Model.Area; using Vanara.PInvoke; namespace BetterGenshinImpact.GameTask.AutoSkip; @@ -38,6 +37,8 @@ public class AutoSkipTrigger : ITaskTrigger public int Priority => 20; public bool IsExclusive => false; + public bool IsBackgroundRunning { get; set; } + private readonly AutoSkipAssets _autoSkipAssets; private readonly AutoSkipConfig _config; @@ -57,6 +58,8 @@ public class AutoSkipTrigger : ITaskTrigger /// private List _selectList = new(); + private PostMessageSimulator? _postMessageSimulator; + public AutoSkipTrigger() { _autoSkipAssets = AutoSkipAssets.Instance; @@ -65,7 +68,9 @@ public class AutoSkipTrigger : ITaskTrigger public void Init() { - IsEnabled = TaskContext.Instance().Config.AutoSkipConfig.Enabled; + IsEnabled = _config.Enabled; + IsBackgroundRunning = _config.RunBackgroundEnabled; + _postMessageSimulator = TaskContext.Instance().PostMessageSimulator; try { @@ -182,7 +187,14 @@ public class AutoSkipTrigger : ITaskTrigger _prevPlayingTime = DateTime.Now; if (TaskContext.Instance().Config.AutoSkipConfig.QuicklySkipConversationsEnabled) { - Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE); + if (IsBackgroundRunning) + { + _postMessageSimulator?.KeyPressBackground(User32.VK.VK_SPACE); + } + else + { + Simulation.SendInput.Keyboard.KeyPress(User32.VK.VK_SPACE); + } } // 对话选项选择 @@ -392,7 +404,7 @@ public class AutoSkipTrigger : ITaskTrigger using var exclamationIconRa = region.Find(_autoSkipAssets.ExclamationIconRo); if (!exclamationIconRa.IsEmpty()) { - TaskControl.Sleep(_config.AfterChooseOptionSleepDelay); + Thread.Sleep(_config.AfterChooseOptionSleepDelay); exclamationIconRa.Click(); AutoSkipLog("点击感叹号选项"); return true; @@ -515,7 +527,7 @@ public class AutoSkipTrigger : ITaskTrigger } // 没OCR到文字,直接选择气泡选项 - TaskControl.Sleep(_config.AfterChooseOptionSleepDelay); + Thread.Sleep(_config.AfterChooseOptionSleepDelay); clickRect.Click(); var msg = _config.IsClickFirstChatOption() ? "第一个" : "最后一个"; AutoSkipLog($"点击{msg}气泡选项"); @@ -531,9 +543,20 @@ public class AutoSkipTrigger : ITaskTrigger { if (string.IsNullOrEmpty(optionType)) { - TaskControl.Sleep(_config.AfterChooseOptionSleepDelay); + Thread.Sleep(_config.AfterChooseOptionSleepDelay); + } + if (IsBackgroundRunning && !SystemControl.IsGenshinImpactActive()) + { + User32.GetCursorPos(out var p); + region.Move(); // 必须移动实际鼠标 + _postMessageSimulator?.LeftButtonClickBackground(); + Thread.Sleep(10); + DesktopRegion.DesktopRegionMove(p.X, p.Y); // 鼠标移动回原来位置 + } + else + { + region.Click(); } - region.Click(); AutoSkipLog(region.Text); } diff --git a/BetterGenshinImpact/GameTask/ITaskTrigger.cs b/BetterGenshinImpact/GameTask/ITaskTrigger.cs index 2dc1bc44..4a816fd7 100644 --- a/BetterGenshinImpact/GameTask/ITaskTrigger.cs +++ b/BetterGenshinImpact/GameTask/ITaskTrigger.cs @@ -6,7 +6,7 @@ /// * 也可以是任务的本身 /// /// 需要短时间内持续循环获取游戏图像的,使用触发器; -/// 需要休眠等待且有一定流程的,应该使用 +/// 需要休眠等待且有一定流程的,应自行实现Task /// public interface ITaskTrigger { @@ -14,6 +14,7 @@ public interface ITaskTrigger /// 触发器名称 /// string Name { get; } + /// /// 是否处于启用状态 /// @@ -29,6 +30,11 @@ public interface ITaskTrigger /// bool IsExclusive { get; } + /// + /// 处于可以后台运行的状态(原神窗口不处于激活状态) + /// + bool IsBackgroundRunning => false; + /// /// 初始化 /// @@ -39,4 +45,4 @@ public interface ITaskTrigger /// /// 捕获的图片等内容 void OnCapture(CaptureContent content); -} \ No newline at end of file +} diff --git a/BetterGenshinImpact/GameTask/Model/Area/DesktopRegion.cs b/BetterGenshinImpact/GameTask/Model/Area/DesktopRegion.cs index 1501dff9..75521d17 100644 --- a/BetterGenshinImpact/GameTask/Model/Area/DesktopRegion.cs +++ b/BetterGenshinImpact/GameTask/Model/Area/DesktopRegion.cs @@ -18,6 +18,12 @@ public class DesktopRegion() : Region(0, 0, PrimaryScreen.WorkingArea.Width, Pri (y + (h * 1d / 2)) * 65535 / Height).LeftButtonClick().Sleep(50).LeftButtonUp(); } + public void DesktopRegionMove(int x, int y, int w, int h) + { + Simulation.SendInput.Mouse.MoveMouseTo((x + (w * 1d / 2)) * 65535 / Width, + (y + (h * 1d / 2)) * 65535 / Height); + } + /// /// 静态方法,每次都会重新计算屏幕大小 /// diff --git a/BetterGenshinImpact/GameTask/Model/Area/Region.cs b/BetterGenshinImpact/GameTask/Model/Area/Region.cs index 3c026dd5..a7bbce27 100644 --- a/BetterGenshinImpact/GameTask/Model/Area/Region.cs +++ b/BetterGenshinImpact/GameTask/Model/Area/Region.cs @@ -111,6 +111,41 @@ public class Region : IDisposable res.TargetRegion.DesktopRegionClick(res.X, res.Y, res.Width, res.Height); } + /// + /// 移动到【自己】的中心 + /// region.Derive(x,y).Move() 等效于 region.MoveTo(x,y) + /// + public void Move() + { + // 相对自己是 0, 0 坐标 + MoveTo(0, 0, Width, Height); + } + + /// + /// 移动到区域内【指定位置】 + /// region.Derive(x,y).Move() 等效于 region.MoveTo(x,y) + /// + /// + /// + public void MoveTo(int x, int y) + { + MoveTo(x, y, 0, 0); + } + + /// + /// 移动到区域内【指定矩形区域】的中心 + /// + /// + /// + /// + /// + /// + public void MoveTo(int x, int y, int w, int h) + { + var res = ConvertRes.ConvertPositionToTargetRegion(x, y, w, h, this); + res.TargetRegion.DesktopRegionMove(res.X, res.Y, res.Width, res.Height); + } + /// /// 直接在遮罩窗口绘制【自己】 /// diff --git a/BetterGenshinImpact/GameTask/TaskContext.cs b/BetterGenshinImpact/GameTask/TaskContext.cs index 603cc4af..de302237 100644 --- a/BetterGenshinImpact/GameTask/TaskContext.cs +++ b/BetterGenshinImpact/GameTask/TaskContext.cs @@ -5,6 +5,7 @@ using BetterGenshinImpact.Helpers; using BetterGenshinImpact.Service; using System; using System.Threading; +using BetterGenshinImpact.Core.Simulator; namespace BetterGenshinImpact.GameTask { @@ -32,6 +33,7 @@ namespace BetterGenshinImpact.GameTask public void Init(IntPtr hWnd) { GameHandle = hWnd; + PostMessageSimulator = Simulation.PostMessage(GameHandle); SystemInfo = new SystemInfo(hWnd); DpiScale = DpiHelper.ScaleY; //MaskWindowHandle = new WindowInteropHelper(MaskWindow.Instance()).Handle; @@ -42,6 +44,8 @@ namespace BetterGenshinImpact.GameTask public IntPtr GameHandle { get; set; } + public PostMessageSimulator PostMessageSimulator { get; private set; } + //public IntPtr MaskWindowHandle { get; set; } public float DpiScale { get; set; } diff --git a/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs b/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs index 11ff0975..f32cd2e7 100644 --- a/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs +++ b/BetterGenshinImpact/GameTask/TaskTriggerDispatcher.cs @@ -268,6 +268,7 @@ namespace BetterGenshinImpact.GameTask } // 检查游戏是否在前台 + var hasBackgroundTriggerToRun = false; var active = SystemControl.IsGenshinImpactActive(); if (!active) { @@ -291,7 +292,24 @@ namespace BetterGenshinImpact.GameTask } _prevGameActive = active; - return; + + if (_triggers != null) + { + var exclusive = _triggers.FirstOrDefault(t => t is { IsEnabled: true, IsExclusive: true }); + if (exclusive != null) + { + hasBackgroundTriggerToRun = exclusive.IsBackgroundRunning; + } + else + { + hasBackgroundTriggerToRun = _triggers.Any(t => t is { IsEnabled: true, IsBackgroundRunning: true }); + } + } + if (!hasBackgroundTriggerToRun) + { + // 没有后台运行的触发器,这次不再进行截图 + return; + } } else { @@ -342,7 +360,7 @@ namespace BetterGenshinImpact.GameTask // 循环执行所有触发器 有独占状态的触发器的时候只执行独占触发器 var content = new CaptureContent(bitmap, _frameIndex, _timer.Interval); - var exclusiveTrigger = _triggers.FirstOrDefault(t => t is { IsEnabled: true, IsExclusive: true }); + var exclusiveTrigger = _triggers!.FirstOrDefault(t => t is { IsEnabled: true, IsExclusive: true }); if (exclusiveTrigger != null) { exclusiveTrigger.OnCapture(content); @@ -350,7 +368,13 @@ namespace BetterGenshinImpact.GameTask } else { - foreach (var trigger in _triggers.Where(trigger => trigger.IsEnabled)) + var runningTriggers = _triggers.Where(t => t.IsEnabled); + if (hasBackgroundTriggerToRun) + { + runningTriggers = runningTriggers.Where(t => t.IsBackgroundRunning); + } + + foreach (var trigger in runningTriggers) { trigger.OnCapture(content); speedTimer.Record(trigger.Name); diff --git a/BetterGenshinImpact/View/Pages/TriggerSettingsPage.xaml b/BetterGenshinImpact/View/Pages/TriggerSettingsPage.xaml index 2c9948d3..34da33aa 100644 --- a/BetterGenshinImpact/View/Pages/TriggerSettingsPage.xaml +++ b/BetterGenshinImpact/View/Pages/TriggerSettingsPage.xaml @@ -222,6 +222,31 @@ + + + + + + + + + + + + + diff --git a/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs index a01a5fdd..b0f86908 100644 --- a/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/HotKeyPageViewModel.cs @@ -12,6 +12,7 @@ using BetterGenshinImpact.GameTask.AutoFight; using BetterGenshinImpact.GameTask.AutoTrackPath; using BetterGenshinImpact.GameTask.Common; using BetterGenshinImpact.GameTask.Common.BgiVision; +using BetterGenshinImpact.GameTask.Model.Area; using BetterGenshinImpact.Helpers.Extensions; using Microsoft.Extensions.Logging; using HotKeySettingModel = BetterGenshinImpact.Model.HotKeySettingModel; @@ -385,8 +386,13 @@ public partial class HotKeyPageViewModel : ObservableObject, IViewModel { postMessageSimulator = Simulation.PostMessage(TaskContext.Instance().GameHandle); } + User32.GetCursorPos(out var p); + Debug.WriteLine($"鼠标位置:{p.X},{p.Y}"); // postMessageSimulator.KeyPressBackground(User32.VK.VK_W); + GameCaptureRegion.GameRegion1080PPosMove(1340, 655); postMessageSimulator.LeftButtonClickBackground(1340, 655); + Thread.Sleep(5); + DesktopRegion.DesktopRegionMove(p.X, p.Y); } )); }