diff --git a/BetterGenshinImpact/Assets/Model/World/bgi_world.onnx b/BetterGenshinImpact/Assets/Model/World/bgi_world.onnx index 1c8820dd..e4af2e34 100644 Binary files a/BetterGenshinImpact/Assets/Model/World/bgi_world.onnx and b/BetterGenshinImpact/Assets/Model/World/bgi_world.onnx differ diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj index 286a9169..ec4eb95b 100644 --- a/BetterGenshinImpact/BetterGenshinImpact.csproj +++ b/BetterGenshinImpact/BetterGenshinImpact.csproj @@ -10,7 +10,7 @@ true Assets\Images\logo.ico BetterGI - 0.39.4 + 0.39.6 x64 embedded diff --git a/BetterGenshinImpact/Core/Config/AllConfig.cs b/BetterGenshinImpact/Core/Config/AllConfig.cs index 7e6a0d42..dcb50edf 100644 --- a/BetterGenshinImpact/Core/Config/AllConfig.cs +++ b/BetterGenshinImpact/Core/Config/AllConfig.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Text.Json.Serialization; using System.Threading.Tasks; +using BetterGenshinImpact.GameTask.AutoTrackPath; namespace BetterGenshinImpact.Core.Config; @@ -175,7 +176,11 @@ public partial class AllConfig : ObservableObject /// 原神按键绑定配置 /// public KeyBindingsConfig KeyBindingsConfig { get; set; } = new(); - + + /// + /// 传送相关配置 + /// + public TpConfig TpConfig { get; set; } = new(); [JsonIgnore] public Action? OnAnyChangedAction { get; set; } @@ -188,7 +193,6 @@ public partial class AllConfig : ObservableObject NotificationConfig.PropertyChanged += OnAnyPropertyChanged; NotificationConfig.PropertyChanged += OnNotificationPropertyChanged; KeyBindingsConfig.PropertyChanged += OnAnyPropertyChanged; - AutoPickConfig.PropertyChanged += OnAnyPropertyChanged; AutoSkipConfig.PropertyChanged += OnAnyPropertyChanged; AutoFishingConfig.PropertyChanged += OnAnyPropertyChanged; @@ -200,7 +204,7 @@ public partial class AllConfig : ObservableObject AutoFightConfig.PropertyChanged += OnAnyPropertyChanged; AutoDomainConfig.PropertyChanged += OnAnyPropertyChanged; AutoMusicGameConfig.PropertyChanged += OnAnyPropertyChanged; - + TpConfig.PropertyChanged += OnAnyPropertyChanged; ScriptConfig.PropertyChanged += OnAnyPropertyChanged; PathingConditionConfig.PropertyChanged += OnAnyPropertyChanged; } diff --git a/BetterGenshinImpact/Core/Config/CommonConfig.cs b/BetterGenshinImpact/Core/Config/CommonConfig.cs index 957012f2..6da8e1f9 100644 --- a/BetterGenshinImpact/Core/Config/CommonConfig.cs +++ b/BetterGenshinImpact/Core/Config/CommonConfig.cs @@ -32,5 +32,11 @@ public partial class CommonConfig : ObservableObject /// 主题 /// [ObservableProperty] - private WindowBackdropType _currentBackdropType = WindowBackdropType.Auto; + private WindowBackdropType _currentBackdropType = WindowBackdropType.Mica; + + /// + /// 是否是第一次运行 + /// + [ObservableProperty] + private bool _isFirstRun = true; } diff --git a/BetterGenshinImpact/GameTask/AutoDomain/AutoDomainTask.cs b/BetterGenshinImpact/GameTask/AutoDomain/AutoDomainTask.cs index 0ebd0030..794764f4 100644 --- a/BetterGenshinImpact/GameTask/AutoDomain/AutoDomainTask.cs +++ b/BetterGenshinImpact/GameTask/AutoDomain/AutoDomainTask.cs @@ -436,6 +436,7 @@ public class AutoDomainTask : ISoloTask finally { Logger.LogInformation("自动战斗线程结束"); + combatScenes.AfterTask(); } }, cts.Token); diff --git a/BetterGenshinImpact/GameTask/AutoFight/AutoFightConfig.cs b/BetterGenshinImpact/GameTask/AutoFight/AutoFightConfig.cs index 4ecfa868..f3ec286f 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/AutoFightConfig.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/AutoFightConfig.cs @@ -25,6 +25,13 @@ public partial class AutoFightConfig : ObservableObject /// [ObservableProperty] private bool _fightFinishDetectEnabled = true; + /// + /// 根据技能CD优化出招人员 + /// 根据填入人或人和cd,来决定当此人元素战技cd未结束时,跳过此人出招,来优化战斗流程,可填入人名或人名数字(用逗号分隔), + /// 多种用分号分隔,例如:白术;钟离,12;,如果人名,则用内置cd检查,如果是人名和数字,则把数字当做出招cd(秒)。 + /// + [ObservableProperty] private string _actionSchedulerByCd = ""; + [Serializable] public partial class FightFinishDetectConfig : ObservableObject { @@ -72,6 +79,40 @@ public partial class AutoFightConfig : ObservableObject [ObservableProperty] private bool _pickDropsAfterFightEnabled = true; + [Serializable] + public partial class PickDropsAfterFightConfig : ObservableObject + { + /// + /// 前进次数 + /// + [ObservableProperty] + private int _forwardTimes = 6; + + /// + /// 校准次数 + /// + [ObservableProperty] + private int _calibrationTimes = 15; + + /// + /// 衰减因子 + /// + [ObservableProperty] + private double _decayFactor = 0.7; + + /// + /// 前进量(秒),设置为0时在[1,3]中随机 + /// + [ObservableProperty] + private int _forwardSeconds = 2; + + } + /// + /// 掉落寻物相关配置 + /// + [ObservableProperty] + private PickDropsAfterFightConfig _pickDropsConfig = new(); + /// /// 战斗结束后,如果存在枫原万叶,则使用该角色捡材料 /// diff --git a/BetterGenshinImpact/GameTask/AutoFight/AutoFightParam.cs b/BetterGenshinImpact/GameTask/AutoFight/AutoFightParam.cs index 72003705..3e4ac777 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/AutoFightParam.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/AutoFightParam.cs @@ -26,6 +26,7 @@ public class AutoFightParam : BaseTaskParam FightFinishDetectEnabled = autoFightConfig.FightFinishDetectEnabled; PickDropsAfterFightEnabled = autoFightConfig.PickDropsAfterFightEnabled; KazuhaPickupEnabled = autoFightConfig.KazuhaPickupEnabled; + ActionSchedulerByCd = autoFightConfig.ActionSchedulerByCd; FinishDetectConfig.FastCheckEnabled = autoFightConfig.FinishDetectConfig.FastCheckEnabled; FinishDetectConfig.FastCheckParams = autoFightConfig.FinishDetectConfig.FastCheckParams; @@ -47,6 +48,7 @@ public class AutoFightParam : BaseTaskParam public int Timeout { get; set; } = 120; public bool KazuhaPickupEnabled = true; + public string ActionSchedulerByCd = ""; } \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/AutoFight/AutoFightTask.cs b/BetterGenshinImpact/GameTask/AutoFight/AutoFightTask.cs index 256c2a40..16ab96c9 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/AutoFightTask.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/AutoFightTask.cs @@ -202,6 +202,7 @@ public class AutoFightTask : ISoloTask throw new Exception("识别队伍角色失败"); } + var actionSchedulerByCd = ParseStringToDictionary(_taskParam.ActionSchedulerByCd); var combatCommands = _combatScriptBag.FindCombatScript(combatScenes.Avatars); // 新的取消token @@ -221,7 +222,8 @@ public class AutoFightTask : ISoloTask return; }*/ var fightEndFlag = false; - string lastFighttName = ""; + string lastFightName = ""; + string skipFightName = ""; //统计切换人打架次数 var countFight = 0; // 战斗操作 @@ -242,6 +244,30 @@ public class AutoFightTask : ISoloTask break; } + //根据元素技能冷却事件优化出招流程,只有当人物切换后才会触发检查 + double skillCd; + if (lastFightName != command.Name && actionSchedulerByCd.TryGetValue(command.Name,out skillCd)) + { + var avatar = combatScenes.Avatars.FirstOrDefault(a => a.Name == command.Name); + if (skillCd < 0) + { + skillCd = FindMax([avatar.SkillCd,avatar.SkillHoldCd]); + } + var dif=(DateTime.UtcNow - avatar.LastSkillTime); + //当技能未冷却时,跳过此次出招 + if ((DateTime.UtcNow -avatar.LastSkillTime).TotalSeconds < skillCd) + { + if (skipFightName != command.Name) + { + Logger.LogInformation($"{command.Name}cd冷却为{skillCd}秒,剩余{skillCd-dif.TotalSeconds}秒,跳过此次行动"); + } + skipFightName = command.Name; + continue; + } + } + + + command.Execute(combatScenes); //统计战斗人次 if (i == combatCommands.Count - 1 || command.Name != combatCommands[i + 1].Name) @@ -249,7 +275,7 @@ public class AutoFightTask : ISoloTask countFight++; } - lastFighttName = command.Name; + lastFightName = command.Name; if (!fightEndFlag && _taskParam is { FightFinishDetectEnabled: true }) { //处于最后一个位置,或者当前执行人和下一个人名字不一样的情况,满足一定条件(开启快速检查,并且检查时间大于0或人名存在配置)检查战斗 @@ -341,7 +367,7 @@ public class AutoFightTask : ISoloTask { var time = DateTime.UtcNow - kazuha.LastSkillTime; //当万叶cd大于3时或战斗人次少于2时(通常无怪物情况下),此时不再触发万叶拾取, - if (!(countFight < 2 || lastFighttName == "枫原万叶" && time.TotalSeconds > 3)) + if (!(countFight < 2 || lastFightName == "枫原万叶" && time.TotalSeconds > 3)) { Logger.LogInformation("使用枫原万叶长E拾取掉落物"); await Delay(300, ct); @@ -470,6 +496,53 @@ public class AutoFightTask : ISoloTask (g >= 240 && g <= 255) && (b >= 240 && b <= 255); } + static double FindMax(double[] numbers) + { + if (numbers == null || numbers.Length == 0) + { + throw new ArgumentException("The array is empty or null."); + } + + double max = numbers[0]>10000 ? 0 : numbers[0]; + foreach (var num in numbers) + { + var cpnum = numbers[0]>10000 ? 0 : num; + max = Math.Max(max, num); + } + + return max; + } + private static Dictionary ParseStringToDictionary(string input,double defaultValue=-1) + { + var dictionary = new Dictionary(); + + if (string.IsNullOrEmpty(input)) + { + return dictionary; // 返回空字典 + } + + string[] pairs = input.Split(';', StringSplitOptions.RemoveEmptyEntries); + + foreach (var pair in pairs) + { + var parts = pair.Split(',', StringSplitOptions.TrimEntries); + + if (parts.Length > 0) + { + string name = parts[0]; + double value = defaultValue; + + if (parts.Length > 1 && double.TryParse(parts[1], out var parsedValue)) + { + value = parsedValue; + } + + dictionary[name] = value; + } + } + + return dictionary; + } private bool HasFightFlagByYolo(ImageRegion imageRegion) { diff --git a/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs b/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs index e9ae9e05..7d4c26a0 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs @@ -31,7 +31,7 @@ public class CombatScenes : IDisposable /// /// 当前配队 /// - public Avatar[] Avatars { get; set; } = Array.Empty(); + public Avatar[] Avatars { get; set; } = []; public Dictionary AvatarMap { get; set; } = []; @@ -70,6 +70,7 @@ public class CombatScenes : IDisposable { throw new Exception("当前处于联机状态,但是队伍人数超过4人,无法识别"); } + // 联机状态下判断 var onePRa = imageRegion.Find(AutoFightAssets.Instance.OnePRa); var p = "p"; @@ -167,7 +168,7 @@ public class CombatScenes : IDisposable if (result.TopClass.Confidence < 0.51) { Cv2.ImWrite(@"log\avatar_side_classify_error.png", src.ToMat()); - throw new Exception($"无法识别第{index}位角色,置信度{result.TopClass.Confidence:F1},结果:{result.TopClass.Name.Name}。请确认您是否阅读了文档中的《快速上手》!"); + throw new Exception($"无法识别第{index}位角色,置信度{result.TopClass.Confidence:F1},结果:{result.TopClass.Name.Name}。请重新阅读了文档中的《快速上手》!"); } } else @@ -175,7 +176,7 @@ public class CombatScenes : IDisposable if (result.TopClass.Confidence < 0.7) { Cv2.ImWrite(@"log\avatar_side_classify_error.png", src.ToMat()); - throw new Exception($"无法识别第{index}位角色,置信度{result.TopClass.Confidence:F1},结果:{result.TopClass.Name.Name}。请确认您是否阅读了文档中的《快速上手》!"); + throw new Exception($"无法识别第{index}位角色,置信度{result.TopClass.Confidence:F1},结果:{result.TopClass.Name.Name}。请重新阅读了文档中的《快速上手》!"); } } @@ -246,6 +247,21 @@ public class CombatScenes : IDisposable } } + public void AfterTask() + { + var mwk = SelectAvatar("玛薇卡"); + if (mwk != null) + { + foreach (var avatar in Avatars) + { + if (avatar.Name != "玛薇卡") + { + avatar.Switch(); + } + } + } + } + public Avatar? SelectAvatar(string name) { return AvatarMap.GetValueOrDefault(name); @@ -381,4 +397,4 @@ public class CombatScenes : IDisposable { _predictor.Dispose(); } -} +} \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/AutoGeniusInvokation/AutoGeniusInvokationTask.cs b/BetterGenshinImpact/GameTask/AutoGeniusInvokation/AutoGeniusInvokationTask.cs index f2610997..731cd993 100644 --- a/BetterGenshinImpact/GameTask/AutoGeniusInvokation/AutoGeniusInvokationTask.cs +++ b/BetterGenshinImpact/GameTask/AutoGeniusInvokation/AutoGeniusInvokationTask.cs @@ -1,5 +1,7 @@ using System.Threading; using System.Threading.Tasks; +using BetterGenshinImpact.GameTask.Common; +using Microsoft.Extensions.Logging; namespace BetterGenshinImpact.GameTask.AutoGeniusInvokation; @@ -9,9 +11,17 @@ public class AutoGeniusInvokationTask(GeniusInvokationTaskParam taskParam) : ISo public Task Start(CancellationToken ct) { - // 读取策略信息 - var duel = ScriptParser.Parse(taskParam.StrategyContent); - duel.Run(ct); + try + { + // 读取策略信息 + var duel = ScriptParser.Parse(taskParam.StrategyContent); + duel.Run(ct); + } + catch (System.Exception e) + { + TaskControl.Logger.LogDebug(e, "执行自动七圣召唤任务异常"); + TaskControl.Logger.LogError("执行自动七圣召唤任务异常:{Exception}", e.Message); + } return Task.CompletedTask; } -} +} \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/AutoGeniusInvokation/ScriptParser.cs b/BetterGenshinImpact/GameTask/AutoGeniusInvokation/ScriptParser.cs index 11fe0f92..fb5cdb37 100644 --- a/BetterGenshinImpact/GameTask/AutoGeniusInvokation/ScriptParser.cs +++ b/BetterGenshinImpact/GameTask/AutoGeniusInvokation/ScriptParser.cs @@ -117,7 +117,7 @@ public class ScriptParser var characterAndSkill = line.Split('{'); var parts = characterAndSkill[0].Split('='); - character.Index = int.Parse(RegexHelper.ExcludeNumberRegex().Replace(parts[0], "")); + character.Index = int.Parse(RegexHelper.ExcludeNumberRegex().Replace(parts[0].Trim(), "")); MyAssert(character.Index >= 1 && character.Index <= 3, "角色序号必须在1-3之间"); if (parts[1].Contains('|')) diff --git a/BetterGenshinImpact/GameTask/AutoPathing/Model/Enum/MoveModeEnum.cs b/BetterGenshinImpact/GameTask/AutoPathing/Model/Enum/MoveModeEnum.cs index 505ea03f..7301da39 100644 --- a/BetterGenshinImpact/GameTask/AutoPathing/Model/Enum/MoveModeEnum.cs +++ b/BetterGenshinImpact/GameTask/AutoPathing/Model/Enum/MoveModeEnum.cs @@ -6,6 +6,7 @@ public class MoveModeEnum(string code, string msg) { public static readonly MoveModeEnum Walk = new("walk", "步行"); public static readonly MoveModeEnum Run = new("run", "奔跑"); + public static readonly MoveModeEnum Dash = new("dash", "持续冲刺"); public static readonly MoveModeEnum Climb = new("climb", "攀爬"); public static readonly MoveModeEnum Fly = new("fly", "飞行"); public static readonly MoveModeEnum Jump = new("jump", "跳跃"); diff --git a/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs b/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs index e6b444d0..82e56065 100644 --- a/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs +++ b/BetterGenshinImpact/GameTask/AutoPathing/PathExecutor.cs @@ -680,7 +680,23 @@ public class PathExecutor // 只有设置为run才会一直疾跑 if (waypoint.MoveMode == MoveModeEnum.Run.Code) { - if (distance > 25) // 距离大于25时可以使用疾跑 + if (distance > 20 != fastMode) // 距离大于20时可以使用疾跑/自由泳 + { + if (fastMode) + { + Simulation.SendInput.SimulateAction(GIActions.SprintMouse, KeyType.KeyUp); + } + else + { + Simulation.SendInput.SimulateAction(GIActions.SprintMouse, KeyType.KeyDown); + } + + fastMode = !fastMode; + } + } + else if (waypoint.MoveMode == MoveModeEnum.Dash.Code) + { + if (distance > 20) // 距离大于25时可以使用疾跑 { if (Math.Abs((fastModeColdTime - DateTime.UtcNow).TotalMilliseconds) > 1000) //冷却一会 { diff --git a/BetterGenshinImpact/GameTask/AutoSkip/Assets/hangout.json b/BetterGenshinImpact/GameTask/AutoSkip/Assets/hangout.json index 677e458f..09f77f9e 100644 --- a/BetterGenshinImpact/GameTask/AutoSkip/Assets/hangout.json +++ b/BetterGenshinImpact/GameTask/AutoSkip/Assets/hangout.json @@ -41,25 +41,25 @@ "我带凯亚去没去过的地方玩吧" ], "卡维结局1:未来蓝图": [ - "一起去说服委托人吧", + "一起去说服刚刚的委托人吧", "尝试一下新的风格" ], "卡维结局2:契约达成": [ - "一起去说服委托人吧", + "一起去说服刚刚的委托人吧", "可以找其他商人筹款" ], "卡维结局3:闲时小聚": [ - "做些别的事转化心情", + "做些别的事转换心情", "卡维为什么要当设计师呢", "去找熟人问问" ], "卡维结局4:群星之下": [ - "做些别的事转化心情", + "做些别的事转换心情", "卡维为什么要当设计师呢", "还是先别管这个了" ], "卡维结局5:第1堂课": [ - "做些别的事转化心情", + "做些别的事转换心情", "找份其他工作赚钱" ], "莱依拉结局1:不可说之事": [ @@ -74,7 +74,7 @@ "莱依拉结局2:代价与回报": [ "虽然有些不靠谱", "我陪你去找答案", - "仙灵应该不想苛责你" + "仙灵应该不只是想苛责你" ], "莱依拉结局3:不愿重蹈覆辙": [ "虽然有些不靠谱", diff --git a/BetterGenshinImpact/GameTask/AutoTrackPath/TpConfig.cs b/BetterGenshinImpact/GameTask/AutoTrackPath/TpConfig.cs new file mode 100644 index 00000000..5187dd84 --- /dev/null +++ b/BetterGenshinImpact/GameTask/AutoTrackPath/TpConfig.cs @@ -0,0 +1,55 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using System; + +namespace BetterGenshinImpact.GameTask.AutoTrackPath; + +public partial class TpConfig : ObservableObject +{ + [ObservableProperty] + private bool _mapZoomEnabled = true; // 地图缩放开关 + + [ObservableProperty] + private int _mapZoomOutDistance = 2000; // 地图缩小的最小距离,单位:像素 + + [ObservableProperty] + private int _mapZoomInDistance = 400; // 地图放大的最大距离,单位:像素 + + [ObservableProperty] + private int _stepIntervalMilliseconds = 20; // 鼠标移动时间间隔,单位:ms + + [ObservableProperty] + private double _maxZoomLevel = 5.0; // 最大缩放等级 + + [ObservableProperty] + private double _minZoomLevel = 1.7; // 最小缩放等级 + + [ObservableProperty] + private double _reviveStatueOfTheSevenPointX = 2296.4; // 七天神像点位X坐标 + + [ObservableProperty] + private double _reviveStatueOfTheSevenPointY = -824.4; // 七天神像点位Y坐标 + + [ObservableProperty] + private int _zoomOutButtonY = 654; // y-coordinate for zoom-out button + + [ObservableProperty] + private int _zoomInButtonY = 428; // y-coordinate for zoom-in button + + [ObservableProperty] + private int _zoomButtonX = 49; // x-coordinate for zoom button + + [ObservableProperty] + private int _zoomStartY = 453; // y-coordinate for zoom start + + [ObservableProperty] + private int _zoomEndY = 628; // y-coordinate for zoom end + + [ObservableProperty] + private double _tolerance = 200; // 允许的移动误差 + + [ObservableProperty] + private int _maxIterations = 30; // 移动最大次数 + + [ObservableProperty] + private int _maxMouseMove = 300; // 单次移动最大距离 +} \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/AutoTrackPath/TpTask.cs b/BetterGenshinImpact/GameTask/AutoTrackPath/TpTask.cs index 824ec549..bce6d9e9 100644 --- a/BetterGenshinImpact/GameTask/AutoTrackPath/TpTask.cs +++ b/BetterGenshinImpact/GameTask/AutoTrackPath/TpTask.cs @@ -1,28 +1,26 @@ -using BetterGenshinImpact.Core.Recognition; +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using BetterGenshinImpact.Core.Recognition; using BetterGenshinImpact.Core.Recognition.OpenCv; +using BetterGenshinImpact.Core.Script.Dependence; using BetterGenshinImpact.Core.Simulator; +using BetterGenshinImpact.Core.Simulator.Extensions; using BetterGenshinImpact.GameTask.AutoGeniusInvokation.Exception; using BetterGenshinImpact.GameTask.Common; using BetterGenshinImpact.GameTask.Common.BgiVision; using BetterGenshinImpact.GameTask.Common.Element.Assets; +using BetterGenshinImpact.GameTask.Common.Exceptions; using BetterGenshinImpact.GameTask.Common.Map; using BetterGenshinImpact.GameTask.Model.Area; using BetterGenshinImpact.GameTask.QuickTeleport.Assets; using BetterGenshinImpact.Helpers.Extensions; using Microsoft.Extensions.Logging; using OpenCvSharp; -using System; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using BetterGenshinImpact.GameTask.Common.Exceptions; using Vanara.PInvoke; using static BetterGenshinImpact.GameTask.Common.TaskControl; -using BetterGenshinImpact.Core.Simulator.Extensions; -using BetterGenshinImpact.GameTask.Common.Job; -using BetterGenshinImpact.Core.Script.Dependence; -using Microsoft.Diagnostics.Utilities; namespace BetterGenshinImpact.GameTask.AutoTrackPath; @@ -34,89 +32,17 @@ public class TpTask(CancellationToken ct) private readonly QuickTeleportAssets _assets = QuickTeleportAssets.Instance; private readonly Rect _captureRect = TaskContext.Instance().SystemInfo.ScaleMax1080PCaptureRect; private readonly double _zoomOutMax1080PRatio = TaskContext.Instance().SystemInfo.ZoomOutMax1080PRatio; - - private static double ReviveStatueOfTheSevenPointX = 2296.4; - private static double ReviveStatueOfTheSevenPointY = -824.4; - - private static int zoomOutButtonY = 654; // y-coordinate for zoom-out button - private static int zoomInButtonY = 428; // y-coordinate for zoom-in button - private static int zoomButtonX = 49; // x-coordinate for zoom button - private static int zoomStartY = 453; // y-coordinate for zoom start - private static int zoomEndY = 628; // y-coordinate for zoom end - private static bool _mapZoomEnabled = true; - private static int _mapZoomOutDistance = 1000; - private static int _mapZoomInDistance = 400; - - private static int _stepIntervalMilliseconds = 20; - - public static bool MapZoomEnabled - { - get => _mapZoomEnabled; - set - { - _mapZoomEnabled = value; - if(!value) - { - Logger.LogInformation("禁用了缩放功能,请自行调整合适的缩放,部分脚本可能会因为禁用缩放功能无法使用"); - } - } - } - - public static int MapZoomOutDistance - { - get => _mapZoomOutDistance; - set - { - if (value < 500 || value > 5000) // 自动设置合理范围 - { - _mapZoomOutDistance = 1000; - } - if (value <= _mapZoomInDistance) - { - _mapZoomOutDistance = _mapZoomInDistance * 2; - } - _mapZoomOutDistance = value; - } - } - - public static int MapZoomInDistance - { - get => _mapZoomInDistance; - set - { - if (value < 200 || value > 1000) // 自动设置合理范围 - { - _mapZoomInDistance = 400; - } - if (value >= _mapZoomOutDistance) - { - _mapZoomInDistance = _mapZoomOutDistance / 2; - } - _mapZoomInDistance = value; - } - } - - public static int StepIntervalMilliseconds - { - get => _stepIntervalMilliseconds; - set - { - if (value < 5 || value > 100) - { - _stepIntervalMilliseconds = 20; - } - _stepIntervalMilliseconds = value; - } - } - + private readonly TpConfig _tpConfig = TaskContext.Instance().Config.TpConfig; + + /// + /// 传送到须弥七天神像 + /// public async Task TpToStatueOfTheSeven() { await CheckInBigMapUi(); - if (_mapZoomEnabled) + if (_tpConfig.MapZoomEnabled) { double currentZoomLevel = GetBigMapZoomLevel(CaptureToRectArea()); - bool tempMapZoomEnable = _mapZoomEnabled; - _mapZoomEnabled = false; // 临时禁用缩放功能 if (currentZoomLevel > 4.5) { await AdjustMapZoomLevel(currentZoomLevel, 4.5); @@ -125,13 +51,8 @@ public class TpTask(CancellationToken ct) { await AdjustMapZoomLevel(currentZoomLevel, 3); } - await Tp(ReviveStatueOfTheSevenPointX, ReviveStatueOfTheSevenPointY); - _mapZoomEnabled = tempMapZoomEnable; - } - else - { - await Tp(ReviveStatueOfTheSevenPointX, ReviveStatueOfTheSevenPointY); } + await Tp(_tpConfig.ReviveStatueOfTheSevenPointX, _tpConfig.ReviveStatueOfTheSevenPointY); } /// /// 通过大地图传送到指定坐标最近的传送点,然后移动到指定坐标 @@ -139,9 +60,10 @@ public class TpTask(CancellationToken ct) /// /// /// 强制以当前的tpX,tpY坐标进行自动传送 - public async Task<(double, double)> TpOnce(double tpX, double tpY, bool force = false) + private async Task<(double, double)> TpOnce(double tpX, double tpY, bool force = false) { var (x, y) = (tpX, tpY); + string? country = null; if (!force) { @@ -152,25 +74,23 @@ public class TpTask(CancellationToken ct) // 计算传送点位置离哪个地图切换后的中心点最近,切换到该地图 await SwitchRecentlyCountryMap(x, y, country); - - // 计算坐标后点击 - var bigMapInAllMapRect = GetBigMapRect(); - if (_mapZoomEnabled) + + if (_tpConfig.MapZoomEnabled) { double zoomLevel = GetBigMapZoomLevel(CaptureToRectArea()); if (zoomLevel > 4.5) { // 显示传送锚点和秘境的缩放等级 await AdjustMapZoomLevel(zoomLevel, 4.5); - Logger.LogInformation("当前缩放等级过大,调整为 {zoomLevel:0.000}", 4.5); + Logger.LogInformation("当前缩放等级过大,调整为 {zoomLevel:0.00}", 4.5); } } - + var bigMapInAllMapRect = GetBigMapRect(); while (!IsPointInBigMapWindow(bigMapInAllMapRect, x, y)) // 左上角 350x400也属于禁止点击区域 { Debug.WriteLine($"({x},{y}) 不在 {bigMapInAllMapRect} 内,继续移动"); Logger.LogInformation("传送点不在当前大地图范围内,继续移动"); - await MoveMapTo(x, y, maxMouseMove: 400); + await MoveMapTo(x, y); await Delay(300, ct); // 等待地图移动完成 bigMapInAllMapRect = GetBigMapRect(); } @@ -318,10 +238,8 @@ public class TpTask(CancellationToken ct) /// /// 目标x坐标 /// 目标y坐标 - /// 允许误差,默认200 - /// 最大尝试次数,默认30 - /// 单次移动最大距离,默认250 - public async Task MoveMapTo(double x, double y, double tolerance = 200, int maxIterations = 30, int maxMouseMove = 250) + + private async Task MoveMapTo(double x, double y) { // 获取当前地图中心点并计算到目标传送点的初始偏移 // await AdjustMapZoomLevel(mapZoomLevel); @@ -333,7 +251,7 @@ public class TpTask(CancellationToken ct) int moveSteps = 10; double totalMoveMouseX = Double.MaxValue; double totalMoveMouseY = Double.MaxValue; - for (int iteration = 0; iteration < maxIterations; iteration++) + for (int iteration = 0; iteration < _tpConfig.MaxIterations; iteration++) { // 尝试移动鼠标 await MouseMoveMap(moveMouseX, moveMouseY, moveSteps); @@ -363,20 +281,22 @@ public class TpTask(CancellationToken ct) totalMoveMouseY = Math.Abs(moveMouseY * yOffset / diffMapY); double mouseDistance = Math.Sqrt(totalMoveMouseX * totalMoveMouseX + totalMoveMouseY * totalMoveMouseY); - if (_mapZoomEnabled) + if (_tpConfig.MapZoomEnabled) { // 调整地图缩放 // mapZoomLevel<5 才显示传送锚点和秘境; mapZoomLevel>1.7 可以避免点错传送锚点 double currentZoomLevel = GetBigMapZoomLevel(CaptureToRectArea()); double oldZoomLevel = currentZoomLevel; - while (mouseDistance > MapZoomOutDistance || mouseDistance < MapZoomInDistance) + while (mouseDistance > _tpConfig.MapZoomOutDistance || mouseDistance < _tpConfig.MapZoomInDistance) { - bool zoomOut = mouseDistance > MapZoomOutDistance; - bool zoomIn = mouseDistance < MapZoomInDistance; - if (zoomOut && currentZoomLevel < 4.0 || zoomIn && currentZoomLevel > 2.7) + bool zoomOut = mouseDistance > _tpConfig.MapZoomOutDistance; + bool zoomIn = mouseDistance < _tpConfig.MapZoomInDistance; + if (zoomOut && currentZoomLevel < _tpConfig.MaxZoomLevel - 1.0 + || zoomIn && currentZoomLevel > _tpConfig.MinZoomLevel + 1.0) { await AdjustMapZoomLevel(zoomIn); + await Delay(50, ct); currentZoomLevel = GetBigMapZoomLevel(CaptureToRectArea()); totalMoveMouseX *= oldZoomLevel / currentZoomLevel; totalMoveMouseY *= oldZoomLevel / currentZoomLevel; @@ -385,9 +305,9 @@ public class TpTask(CancellationToken ct) } else { - double targetZoom = zoomIn ? 1.7 : 5.0; + double targetZoom = zoomIn ? _tpConfig.MinZoomLevel : _tpConfig.MaxZoomLevel; // 考虑调整和识别误差,所以相差0.05就不再调整。 - if (currentZoomLevel > 4.95 || currentZoomLevel < 1.75) + if (currentZoomLevel > _tpConfig.MaxZoomLevel - 0.05 || currentZoomLevel < _tpConfig.MinZoomLevel + 0.05) { break; } @@ -398,21 +318,20 @@ public class TpTask(CancellationToken ct) } // 非常接近目标点,不再进一步调整 - if (mouseDistance < tolerance) + if (mouseDistance < _tpConfig.Tolerance) { Logger.LogInformation("移动 {I} 次鼠标后,已经接近目标点,不再移动地图。", iteration + 1); break; } - - // 单次移动最大距离为 maxMouseMove - moveMouseX = (int)Math.Min(totalMoveMouseX, maxMouseMove * totalMoveMouseX / mouseDistance) * Math.Sign(xOffset); - moveMouseY = (int)Math.Min(totalMoveMouseY, maxMouseMove * totalMoveMouseY / mouseDistance) * Math.Sign(yOffset); + + moveMouseX = (int)Math.Min(totalMoveMouseX, _tpConfig.MaxMouseMove * totalMoveMouseX / mouseDistance) * Math.Sign(xOffset); + moveMouseY = (int)Math.Min(totalMoveMouseY, _tpConfig.MaxMouseMove * totalMoveMouseY / mouseDistance) * Math.Sign(yOffset); double moveMouseLength = Math.Sqrt(moveMouseX * moveMouseX + moveMouseY * moveMouseY); moveSteps = Math.Max((int)moveMouseLength / 10, 3); // 每次移动的步数最小为3,避免除0错误 } else { - Logger.LogDebug($"第 {iteration} 次移动鼠标失败,可能是点击了传送点或者其他交互对象。"); + Logger.LogDebug($"第 {iteration + 1} 次移动鼠标失败,可能是点击了传送点或者其他交互对象。"); } } } @@ -440,25 +359,26 @@ public class TpTask(CancellationToken ct) /// 调整地图缩放级别以加速移动 /// /// 是否放大地图 - public async Task AdjustMapZoomLevel(bool zoomIn) + private async Task AdjustMapZoomLevel(bool zoomIn) { if (zoomIn) { - GameCaptureRegion.GameRegionClick((rect, scale) => (zoomButtonX * scale, zoomInButtonY * scale)); + GameCaptureRegion.GameRegionClick((rect, scale) => (_tpConfig.ZoomButtonX * scale, _tpConfig.ZoomInButtonY * scale)); } else { - GameCaptureRegion.GameRegionClick((rect, scale) => (zoomButtonX * scale, zoomOutButtonY * scale)); + GameCaptureRegion.GameRegionClick((rect, scale) => (_tpConfig.ZoomButtonX * scale, _tpConfig.ZoomOutButtonY * scale)); } await Delay(50, ct); } - [Obsolete] + /// /// 调整地图的缩放等级(整数缩放级别)。 /// /// 目标等级:1-6。整数。随着数字变大地图越小,细节越少。 + [Obsolete] public async Task AdjustMapZoomLevel(int zoomLevel) { for (int i = 0; i < 5; i++) @@ -478,15 +398,15 @@ public class TpTask(CancellationToken ct) /// /// 当前缩放等级:1.0-6.0,浮点数。 /// 目标缩放等级:1.0-6.0,浮点数。 - public async Task AdjustMapZoomLevel(double zoomLevel, double targetZoomLevel) + private async Task AdjustMapZoomLevel(double zoomLevel, double targetZoomLevel) { // Logger.LogInformation("调整地图缩放等级:{zoomLevel:0.000} -> {targetZoomLevel:0.000}", zoomLevel, targetZoomLevel); - int initialY = (int)(zoomStartY + (zoomEndY - zoomStartY) * (zoomLevel - 1) / 5d); - int targetY = (int)(zoomStartY + (zoomEndY - zoomStartY) * (targetZoomLevel - 1) / 5d); - await MouseClickAndMove(zoomButtonX, initialY, zoomButtonX, targetY); + int initialY = (int)(_tpConfig.ZoomStartY + (_tpConfig.ZoomEndY - _tpConfig.ZoomStartY) * (zoomLevel - 1) / 5d); + int targetY = (int)(_tpConfig.ZoomStartY + (_tpConfig.ZoomEndY - _tpConfig.ZoomStartY) * (targetZoomLevel - 1) / 5d); + await MouseClickAndMove(_tpConfig.ZoomButtonX, initialY, _tpConfig.ZoomButtonX, targetY); } - public async Task MouseMoveMap(int pixelDeltaX, int pixelDeltaY, int steps = 10) + private async Task MouseMoveMap(int pixelDeltaX, int pixelDeltaY, int steps = 10) { // 确保不影响总移动距离 int totalX = 0; @@ -506,8 +426,8 @@ public class TpTask(CancellationToken ct) } // 均匀分配多余的部分到前半段 - int remainingX = (int)(pixelDeltaX - totalX); - int remainingY = (int)(pixelDeltaY - totalY); + int remainingX = (pixelDeltaX - totalX); + int remainingY = (pixelDeltaY - totalY); for (int i = 0; i < steps / 2 + 1; i++) { stepX[i] += remainingX / (steps / 2 + 1) + ((remainingX % (steps / 2 + 1) > i) ? 0 : 1); @@ -523,7 +443,7 @@ public class TpTask(CancellationToken ct) for (var i = 0; i < steps; i++) { GlobalMethod.MoveMouseBy(stepX[i], stepY[i]); - await Delay(StepIntervalMilliseconds, ct); + await Delay(_tpConfig.StepIntervalMilliseconds, ct); } GlobalMethod.LeftButtonUp(); } @@ -604,7 +524,7 @@ public class TpTask(CancellationToken ct) } /// - /// 获取最近的传送点位置 + /// 获取最近的传送点位置和所处区域 /// /// /// @@ -820,4 +740,4 @@ public class TpTask(CancellationToken ct) // 1~6 的缩放等级 return (-5 * s) + 6; } -} \ No newline at end of file +} diff --git a/BetterGenshinImpact/GameTask/AutoWood/AutoWoodTask.cs b/BetterGenshinImpact/GameTask/AutoWood/AutoWoodTask.cs index 7b3146a2..2834f97a 100644 --- a/BetterGenshinImpact/GameTask/AutoWood/AutoWoodTask.cs +++ b/BetterGenshinImpact/GameTask/AutoWood/AutoWoodTask.cs @@ -16,6 +16,7 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using BetterGenshinImpact.Core.Simulator.Extensions; using Vanara.PInvoke; using static BetterGenshinImpact.GameTask.Common.TaskControl; using static Vanara.PInvoke.User32; @@ -30,7 +31,7 @@ public partial class AutoWoodTask : ISoloTask { public string Name => "自动伐木"; - private readonly AutoWoodAssets _assets; + private AutoWoodAssets _assets; private bool _first = true; @@ -38,7 +39,7 @@ public partial class AutoWoodTask : ISoloTask private readonly Login3rdParty _login3rdParty; - private VK _zKey = VK.VK_Z; + // private VK _zKey = VK.VK_Z; private readonly WoodTaskParam _taskParam; @@ -49,12 +50,13 @@ public partial class AutoWoodTask : ISoloTask this._taskParam = taskParam; _login3rdParty = new(); AutoWoodAssets.DestroyInstance(); - _assets = AutoWoodAssets.Instance; + _printer = new WoodStatisticsPrinter(_assets); } public Task Start(CancellationToken ct) { + _assets = AutoWoodAssets.Instance; var runTimeWatch = new Stopwatch(); _ct = ct; _printer.Ct = _ct; @@ -70,20 +72,20 @@ public partial class AutoWoodTask : ISoloTask Logger.LogInformation("自动伐木启用B服模式"); } - SettingsContainer settingsContainer = new(); - - if (settingsContainer.OverrideController?.KeyboardMap?.ActionElementMap.Where(item => item.ActionId == ActionId.Gadget).FirstOrDefault()?.ElementIdentifierId is ElementIdentifierId key) - { - if (key != ElementIdentifierId.Z) - { - _zKey = key.ToVK(); - Logger.LogInformation($"自动伐木检测到用户改键 {ElementIdentifierId.Z.ToName()} 改为 {key.ToName()}"); - if (key == ElementIdentifierId.LeftShift || key == ElementIdentifierId.RightShift) - { - Logger.LogInformation($"用户改键 {key.ToName()} 可能不受模拟支持,若使用正常则忽略"); - } - } - } + // SettingsContainer settingsContainer = new(); + // + // if (settingsContainer.OverrideController?.KeyboardMap?.ActionElementMap.Where(item => item.ActionId == ActionId.Gadget).FirstOrDefault()?.ElementIdentifierId is ElementIdentifierId key) + // { + // if (key != ElementIdentifierId.Z) + // { + // _zKey = key.ToVK(); + // Logger.LogInformation($"自动伐木检测到用户改键 {ElementIdentifierId.Z.ToName()} 改为 {key.ToName()}"); + // if (key == ElementIdentifierId.LeftShift || key == ElementIdentifierId.RightShift) + // { + // Logger.LogInformation($"用户改键 {key.ToName()} 可能不受模拟支持,若使用正常则忽略"); + // } + // } + // } SystemControl.ActivateWindow(); // 伐木开始计时 @@ -168,6 +170,7 @@ public partial class AutoWoodTask : ISoloTask TaskContext.Instance().Config.AutoWoodConfig.WoodCountOcrEnabled = false; throw new NormalEndException("首次伐木就未识别到木材数据,已经自动关闭【OCR识别并累计木材数】的功能,请重新启动【自动伐木】功能!"); } + return; } @@ -210,6 +213,7 @@ public partial class AutoWoodTask : ISoloTask return _firstWoodOcrText; } } + stopwatch.Stop(); // 停止计时 _firstWoodOcrText = FindBestOcrResult(firstOcrResultList); return _firstWoodOcrText; @@ -234,6 +238,7 @@ public partial class AutoWoodTask : ISoloTask return !string.IsNullOrEmpty(recognizedText) && recognizedText.Contains("获得"); } + return !string.IsNullOrEmpty(recognizedText) && recognizedText.Contains("获得") && (recognizedText.Contains('×') || recognizedText.Contains('x')); @@ -291,6 +296,7 @@ public partial class AutoWoodTask : ISoloTask Logger.LogWarning("未知的木材名:{woodName},数量{Cnt}", materialName, quantity); return; } + WoodTotalDict.AddOrUpdate( key: materialName, addValue: quantity, @@ -334,6 +340,7 @@ public partial class AutoWoodTask : ISoloTask isFound = false; continue; } + var materialName = match.Groups[1].Value.Trim(); Debug.WriteLine($"第一次获取的木材名称:{materialName}"); if (!ExistWoods.Contains(materialName)) @@ -417,14 +424,14 @@ public partial class AutoWoodTask : ISoloTask throw new NormalEndException("请先装备小道具「王树瑞佑」!"); #else System.Threading.Thread.Sleep(2000); - Simulation.SendInput.Keyboard.KeyPress(_zKey); + Simulation.SendInput.SimulateAction(GIActions.QuickUseGadget); Debug.WriteLine("[AutoWood] Z"); _first = false; #endif } else { - Simulation.SendInput.Keyboard.KeyPress(_zKey); + Simulation.SendInput.SimulateAction(GIActions.QuickUseGadget); Debug.WriteLine("[AutoWood] Z"); _first = false; } @@ -445,7 +452,7 @@ public partial class AutoWoodTask : ISoloTask #endif } - Simulation.SendInput.Keyboard.KeyPress(_zKey); + Simulation.SendInput.SimulateAction(GIActions.QuickUseGadget); Debug.WriteLine("[AutoWood] Z"); Sleep(500, _ct); }, TimeSpan.FromSeconds(1), 120); @@ -542,4 +549,4 @@ public partial class AutoWoodTask : ISoloTask throw new RetryException("未检测进入游戏界面"); } } -} +} \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/Common/Job/GoToAdventurersGuildTask.cs b/BetterGenshinImpact/GameTask/Common/Job/GoToAdventurersGuildTask.cs index f7bb5ae9..e8e5ecd4 100644 --- a/BetterGenshinImpact/GameTask/Common/Job/GoToAdventurersGuildTask.cs +++ b/BetterGenshinImpact/GameTask/Common/Job/GoToAdventurersGuildTask.cs @@ -31,14 +31,14 @@ public class GoToAdventurersGuildTask { try { - // 合成完毕后领取奖励 - await new ClaimEncounterPointsRewardsTask().Start(ct); - // 如果有好感队伍名称,先切换到好感队伍 if (!string.IsNullOrEmpty(dailyRewardPartyName)) { await new SwitchPartyTask().Start(dailyRewardPartyName, ct); } + + // F1领取奖励 + await new ClaimEncounterPointsRewardsTask().Start(ct); await DoOnce(country, ct); break; diff --git a/BetterGenshinImpact/GameTask/Common/Job/ScanPickTask.cs b/BetterGenshinImpact/GameTask/Common/Job/ScanPickTask.cs index 1810c0e4..f0f6758a 100644 --- a/BetterGenshinImpact/GameTask/Common/Job/ScanPickTask.cs +++ b/BetterGenshinImpact/GameTask/Common/Job/ScanPickTask.cs @@ -44,13 +44,15 @@ public class ScanPickTask public async Task DoOnce(CancellationToken ct) { - await ResetCamera(ct); - - for (int n = 0; n < 5; n++) // 最多跑5次 + var forwardTimes = TaskContext.Instance().Config.AutoFightConfig.PickDropsConfig.ForwardTimes; + for (int n = 0; n < forwardTimes; n++) // 直走次数 { + await ResetCamera(ct); var hasDrops = false; // 旋转视角 + var step = 300 * _dpi; // TODO:把300换成一个更加普适的值 + step = n % 2 == 0 ? step : -step; for (var i = 0; i < 20; i++) { var ra = CaptureToRectArea(); @@ -63,18 +65,14 @@ public class ScanPickTask if (pickItems.Count > 0) { hasDrops = true; - // 把鼠标位置和物品位置重合 - MoveCursorTo(pickItems.First(), ra); - await Delay(100, ct); - // 物体越小,距离越远 - await WalkForward(ct); + await MoveTowardsFirstDrop(ct, 300 * _dpi); break; } - Simulation.SendInput.Mouse.MoveMouseBy((int)(300 * _dpi), 0); + Simulation.SendInput.Mouse.MoveMouseBy((int)step, 0); await Delay(100, ct); } - + if (!hasDrops) { break; @@ -83,13 +81,56 @@ public class ScanPickTask } - private static async Task WalkForward(CancellationToken ct) + private static async Task WalkForward(CancellationToken ct, int ms = 1000) { Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyDown); - await Delay(1000, ct); + await Delay(ms, ct); Simulation.SendInput.SimulateAction(GIActions.MoveForward, KeyType.KeyUp); } + private async Task MoveTowardsFirstDrop(CancellationToken ct, double step) + { + //通过每次缩小之前的步长来定位,可能有一定开销 + var decayFactor = TaskContext.Instance().Config.AutoFightConfig.PickDropsConfig.DecayFactor; + var calibrationTimes = TaskContext.Instance().Config.AutoFightConfig.PickDropsConfig.CalibrationTimes; + + var found = false; + for (var i = 0; i < calibrationTimes; i++) + { + var ra = CaptureToRectArea(); + var resultDic = _predictor.Detect(ra); + var pickItems = resultDic.Where(x => x.Key is "drops" or "ore") + .SelectMany(x => x.Value).ToList(); + if (pickItems.Count > 0) + { + step *= decayFactor; + found = true; + //只关心横坐标 + var centerX = (pickItems.First().Left + pickItems.First().Right) / 2; + var dx = centerX - ra.Width / 2; + if (dx > 0) + Simulation.SendInput.Mouse.MoveMouseBy((int)step, 0); + else if (dx < 0) + Simulation.SendInput.Mouse.MoveMouseBy(-(int)step, 0); + await Delay(100, ct); + } + else + { + //也许已经对准,被人物挡住 + break; + } + } + if (found) //仅在找到物品时前进(在误判进入该函数时避免远离原地) + { + Logger.LogInformation("前进采集"); + var forwardms = TaskContext.Instance().Config.AutoFightConfig.PickDropsConfig.ForwardSeconds * 1000; + if (forwardms == 0) + forwardms = new Random().Next(1000, 3000); + + await WalkForward(ct, forwardms); + } + } + private void MoveCursorTo(Rect item, ImageRegion ra) { var centerX = (item.Left + item.Right) / 2; @@ -103,8 +144,8 @@ public class ScanPickTask // 回正 并下移视角 private async Task ResetCamera(CancellationToken ct) { - // Simulation.SendInput.Keyboard.Mouse.MiddleButtonClick(); - // await Delay(500, ct); + Simulation.SendInput.Keyboard.Mouse.MiddleButtonClick(); + await Delay(500, ct); Simulation.SendInput.Keyboard.Mouse.MoveMouseBy(0, (int)(500 * _dpi)); await Delay(100, ct); } diff --git a/BetterGenshinImpact/GameTask/LogParse/LogParse.cs b/BetterGenshinImpact/GameTask/LogParse/LogParse.cs index 8feb53e5..83ef540e 100644 --- a/BetterGenshinImpact/GameTask/LogParse/LogParse.cs +++ b/BetterGenshinImpact/GameTask/LogParse/LogParse.cs @@ -325,7 +325,8 @@ namespace LogParse (name: "最后小怪日期", value: ms => ms.LastSmallTime), (name: "精英", value: ms => ms.EliteGameStatistics.ToString()), (name: "精英详细", value: ms => ms.EliteDetails), (name: "最后精英日期", value: ms => ms.LastEliteTime), - (name: "总计锄地摩拉", value: ms => ms.TotalMoraKillingMonstersMora.ToString()) + (name: "总计锄地摩拉", value: ms => ms.TotalMoraKillingMonstersMora.ToString()), + (name: "突发事件获取摩拉", value: ms => ms.EmergencyBonus) ]; //锄地部分新曾字段 (string name, Func value)[] col2Configs=[..msColConfigs.ToList().Where(item=>item.name!="日期" && item.name!="最后小怪日期" && item.name!="最后精英日期"), @@ -386,7 +387,7 @@ namespace LogParse .OrderBy(group => group.Key) .Reverse().ToList(); - html.AppendLine($"

按日锄地摩拉统计

"); + html.AppendLine($"

按日摩拉收益统计

"); html.AppendLine(""); html.AppendLine(" "); diff --git a/BetterGenshinImpact/GameTask/LogParse/MoraStatistics.cs b/BetterGenshinImpact/GameTask/LogParse/MoraStatistics.cs index 50ca2b3e..ae62e5ea 100644 --- a/BetterGenshinImpact/GameTask/LogParse/MoraStatistics.cs +++ b/BetterGenshinImpact/GameTask/LogParse/MoraStatistics.cs @@ -25,6 +25,16 @@ namespace LogParse public List SmallMonsterActionItems => this.MonsterActionItems.Except(EliteMonsterActionItems).ToList(); + public string EmergencyBonus + { + get + { + var ls = this.ActionItems.Where(item => item.ActionId == 28).ToList(); + var count = ls.Count(); + return ls.Sum(item=>item.Num)+((count==0 || count>=10)?"":$"({count}/10)"); + } + } + public string LastEliteTime => EliteMonsterActionItems.MaxBy(item => item?.Time)?.Time ?? null; public string LastSmallTime => SmallMonsterActionItems.MaxBy(item => item?.Time)?.Time ?? null; diff --git a/BetterGenshinImpact/GameTask/LogParse/TravelsDiaryDetailManager.cs b/BetterGenshinImpact/GameTask/LogParse/TravelsDiaryDetailManager.cs index a8db8801..721192f5 100644 --- a/BetterGenshinImpact/GameTask/LogParse/TravelsDiaryDetailManager.cs +++ b/BetterGenshinImpact/GameTask/LogParse/TravelsDiaryDetailManager.cs @@ -50,8 +50,8 @@ public class TravelsDiaryDetailManager var _temp = JsonSerializer.Deserialize>(File.ReadAllText(tddfile)); if (_temp != null) { - //只统计杀怪的 - actionItems.AddRange(_temp.Data.List.Where(item => item.ActionId == 37)); + //统计杀怪或突发事件奖励 + actionItems.AddRange(_temp.Data.List.Where(item => item.ActionId == 37 || item.ActionId == 28)); } } } diff --git a/BetterGenshinImpact/GameTask/QuickTeleport/Assets/1920x1080/ObsidianTotemPole.png b/BetterGenshinImpact/GameTask/QuickTeleport/Assets/1920x1080/ObsidianTotemPole.png new file mode 100644 index 00000000..9cf0f052 Binary files /dev/null and b/BetterGenshinImpact/GameTask/QuickTeleport/Assets/1920x1080/ObsidianTotemPole.png differ diff --git a/BetterGenshinImpact/GameTask/QuickTeleport/Assets/QuickTeleportAssets.cs b/BetterGenshinImpact/GameTask/QuickTeleport/Assets/QuickTeleportAssets.cs index 0fa74470..07028321 100644 --- a/BetterGenshinImpact/GameTask/QuickTeleport/Assets/QuickTeleportAssets.cs +++ b/BetterGenshinImpact/GameTask/QuickTeleport/Assets/QuickTeleportAssets.cs @@ -34,6 +34,7 @@ public class QuickTeleportAssets : BaseAssets BuildMapChooseIconRo("StatueOfTheSeven.png"), BuildMapChooseIconRo("Domain.png"), BuildMapChooseIconRo("Domain2.png"), + BuildMapChooseIconRo("ObsidianTotemPole.png"), BuildMapChooseIconRo("PortableWaypoint.png"), BuildMapChooseIconRo("Mansion.png"), BuildMapChooseIconRo("SubSpaceWaypoint.png"), @@ -140,7 +141,6 @@ public class QuickTeleportAssets : BaseAssets /// public RecognitionObject BuildMapChooseIconRo(string name) { - var info = TaskContext.Instance().SystemInfo; var ro = new RecognitionObject { Name = name + "MapChooseIcon", diff --git a/BetterGenshinImpact/GameTask/TaskContext.cs b/BetterGenshinImpact/GameTask/TaskContext.cs index 1f4ea76e..fd3023e6 100644 --- a/BetterGenshinImpact/GameTask/TaskContext.cs +++ b/BetterGenshinImpact/GameTask/TaskContext.cs @@ -65,7 +65,7 @@ namespace BetterGenshinImpact.GameTask } } - public SettingsContainer? GameSettings { get; set; } + // public SettingsContainer? GameSettings { get; set; } /// /// 关联启动原神的时间 diff --git a/BetterGenshinImpact/GameTask/TaskRunner.cs b/BetterGenshinImpact/GameTask/TaskRunner.cs index f5aacc6d..f0902164 100644 --- a/BetterGenshinImpact/GameTask/TaskRunner.cs +++ b/BetterGenshinImpact/GameTask/TaskRunner.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; using BetterGenshinImpact.Helpers; using Wpf.Ui.Violeta.Controls; using static BetterGenshinImpact.GameTask.Common.TaskControl; -using BetterGenshinImpact.Core.Recorder; +using BetterGenshinImpact.Service; namespace BetterGenshinImpact.GameTask; @@ -119,6 +119,8 @@ public class TaskRunner public async Task RunSoloTaskAsync(ISoloTask soloTask) { + // 没启动的时候先启动 + await ScriptService.StartGameTask(); await Task.Run(() => RunCurrentAsync(async () => await soloTask.Start(CancellationContext.Instance.Cts.Token))); } diff --git a/BetterGenshinImpact/Helpers/DirectoryHelper.cs b/BetterGenshinImpact/Helpers/DirectoryHelper.cs index 2fa3aab9..00f9df40 100644 --- a/BetterGenshinImpact/Helpers/DirectoryHelper.cs +++ b/BetterGenshinImpact/Helpers/DirectoryHelper.cs @@ -33,6 +33,15 @@ public class DirectoryHelper private static void DeleteDirectory(DirectoryInfo directoryInfo) { + + //通过软链接生成的目录,直接删除该链接目录,而不涉及其文件本体 + var attributes = directoryInfo.Attributes; + if ((attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) + { + directoryInfo.Delete(); + return; + } + // 递归处理子目录 foreach (var subDirectory in directoryInfo.GetDirectories()) { @@ -80,4 +89,54 @@ public class DirectoryHelper RemoveReadOnlyAttribute(subDirectory); } } + + public static void CopyDirectory(string sourceDir, string destDir) + { + // 创建目标目录 + Directory.CreateDirectory(destDir); + + // 获取源目录中的所有文件 + foreach (var file in Directory.GetFiles(sourceDir)) + { + var destFile = Path.Combine(destDir, Path.GetFileName(file)); + File.Copy(file, destFile, true); // 覆盖同名文件 + } + + // 获取源目录中的所有子目录 + foreach (var subDir in Directory.GetDirectories(sourceDir)) + { + var destSubDir = Path.Combine(destDir, Path.GetFileName(subDir)); + CopyDirectory(subDir, destSubDir); // 递归拷贝子目录 + } + } + + /// + /// 递归删除指定目录及其所有子目录和文件 + /// + /// 要删除的目录的路径 + public static void DeleteDirectoryRecursively(string directoryPath) + { + // 检查目录是否存在 + if (Directory.Exists(directoryPath)) + { + // 获取目录中的所有子目录 + string[] subDirectories = Directory.GetDirectories(directoryPath); + foreach (string subDirectory in subDirectories) + { + // 递归调用删除子目录 + DeleteDirectoryRecursively(subDirectory); + } + + // 获取目录中的所有文件 + string[] files = Directory.GetFiles(directoryPath); + foreach (string file in files) + { + // 删除文件 + File.Delete(file); + } + + // 删除空目录 + Directory.Delete(directoryPath); + } + } } diff --git a/BetterGenshinImpact/Helpers/Ui/WindowHelper.cs b/BetterGenshinImpact/Helpers/Ui/WindowHelper.cs index f0820b3c..36cd3278 100644 --- a/BetterGenshinImpact/Helpers/Ui/WindowHelper.cs +++ b/BetterGenshinImpact/Helpers/Ui/WindowHelper.cs @@ -1,4 +1,5 @@ -using System.Windows.Media; +using System.Diagnostics; +using System.Windows.Media; using BetterGenshinImpact.GameTask; using Wpf.Ui.Controls; @@ -8,14 +9,16 @@ public class WindowHelper { public static void TryApplySystemBackdrop(System.Windows.Window window) { - window.Background = new SolidColorBrush(Colors.Transparent); - if (WindowBackdrop.IsSupported(TaskContext.Instance().Config.CommonConfig.CurrentBackdropType)) { if (TaskContext.Instance().Config.CommonConfig.CurrentBackdropType == WindowBackdropType.Acrylic) { window.Background = new SolidColorBrush(Color.FromArgb(100, 0, 0, 0)); } + else + { + window.Background = new SolidColorBrush(Colors.Transparent); + } WindowBackdrop.ApplyBackdrop(window, TaskContext.Instance().Config.CommonConfig.CurrentBackdropType); return; @@ -24,14 +27,12 @@ public class WindowHelper if (WindowBackdrop.IsSupported(WindowBackdropType.Mica)) { + window.Background = new SolidColorBrush(Colors.Transparent); WindowBackdrop.ApplyBackdrop(window, WindowBackdropType.Mica); } - else if (WindowBackdrop.IsSupported(WindowBackdropType.Tabbed)) - { - WindowBackdrop.ApplyBackdrop(window, WindowBackdropType.Tabbed); - } else if (WindowBackdrop.IsSupported(WindowBackdropType.Acrylic)) { + window.Background = new SolidColorBrush(Color.FromArgb(100, 0, 0, 0)); WindowBackdrop.ApplyBackdrop(window, WindowBackdropType.Acrylic); } } diff --git a/BetterGenshinImpact/Service/UpdateService.cs b/BetterGenshinImpact/Service/UpdateService.cs index 5f4d72f4..a483ccff 100644 --- a/BetterGenshinImpact/Service/UpdateService.cs +++ b/BetterGenshinImpact/Service/UpdateService.cs @@ -93,14 +93,14 @@ public class UpdateService : IUpdateService case CheckUpdateWindow.CheckUpdateWindowButton.Update: { // 唤起更新程序 - string updaterExePath = Global.Absolute("updater.exe"); + string updaterExePath = Global.Absolute("BetterGI.update.exe"); if (!File.Exists(updaterExePath)) { await MessageBox.ErrorAsync("更新程序不存在,请选择其他更新方式!"); return; } // 启动 - Process.Start(updaterExePath); + Process.Start(updaterExePath, "-I"); // 退出程序 Application.Current.Shutdown(); diff --git a/BetterGenshinImpact/View/MaskWindow.xaml.cs b/BetterGenshinImpact/View/MaskWindow.xaml.cs index f341722f..0c772f67 100644 --- a/BetterGenshinImpact/View/MaskWindow.xaml.cs +++ b/BetterGenshinImpact/View/MaskWindow.xaml.cs @@ -176,26 +176,26 @@ public partial class MaskWindow : Window } // 读取游戏注册表配置 - ReadGameSettings(); + // ReadGameSettings(); } - private void ReadGameSettings() - { - try - { - SettingsContainer settings = new(); - TaskContext.Instance().GameSettings = settings; - var lang = settings.Language?.TextLang; - if (lang != null && lang != TextLanguage.SimplifiedChinese) - { - _logger.LogWarning("当前游戏语言{Lang}不是简体中文,部分功能可能无法正常使用。The game language is not Simplified Chinese, some functions may not work properly", lang); - } - } - catch (Exception e) - { - _logger.LogWarning("游戏注册表配置信息读取失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); - } - } + // private void ReadGameSettings() + // { + // try + // { + // SettingsContainer settings = new(); + // TaskContext.Instance().GameSettings = settings; + // var lang = settings.Language?.TextLang; + // if (lang != null && lang != TextLanguage.SimplifiedChinese) + // { + // _logger.LogWarning("当前游戏语言{Lang}不是简体中文,部分功能可能无法正常使用。The game language is not Simplified Chinese, some functions may not work properly", lang); + // } + // } + // catch (Exception e) + // { + // _logger.LogWarning("游戏注册表配置信息读取失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); + // } + // } protected override void OnSourceInitialized(EventArgs e) { diff --git a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml index 8f419032..24815d17 100644 --- a/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml +++ b/BetterGenshinImpact/View/Pages/TaskSettingsPage.xaml @@ -428,7 +428,34 @@ - + + + + + + + + + + + + + @@ -624,33 +651,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + diff --git a/BetterGenshinImpact/View/Pages/TriggerSettingsPage.xaml b/BetterGenshinImpact/View/Pages/TriggerSettingsPage.xaml index 73040063..66b65fc9 100644 --- a/BetterGenshinImpact/View/Pages/TriggerSettingsPage.xaml +++ b/BetterGenshinImpact/View/Pages/TriggerSettingsPage.xaml @@ -778,7 +778,166 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BetterGenshinImpact/View/Pages/View/ScriptGroupConfigView.xaml b/BetterGenshinImpact/View/Pages/View/ScriptGroupConfigView.xaml index 6975a47c..970e8715 100644 --- a/BetterGenshinImpact/View/Pages/View/ScriptGroupConfigView.xaml +++ b/BetterGenshinImpact/View/Pages/View/ScriptGroupConfigView.xaml @@ -370,6 +370,34 @@ + + + + + + + + + + + + + WindowBackdropType.Mica, WindowBackdropType.Mica => WindowBackdropType.Acrylic, - WindowBackdropType.Acrylic => WindowBackdropType.Tabbed, - WindowBackdropType.Tabbed => WindowBackdropType.Auto, - _ => WindowBackdropType.Auto + WindowBackdropType.Acrylic => WindowBackdropType.Mica, + _ => WindowBackdropType.Acrylic }; Config.CommonConfig.CurrentBackdropType = CurrentBackdropType; if (Application.Current.MainWindow is MainWindow mainWindow) { - mainWindow.Background = new SolidColorBrush(Color.FromArgb(100, 0, 0, 0));; + mainWindow.Background = new SolidColorBrush(Color.FromArgb(100, 0, 0, 0)); WindowBackdrop.ApplyBackdrop(mainWindow, CurrentBackdropType); } } @@ -92,8 +98,78 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel } [RelayCommand] - [SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", "MVVMTK0039:Async void returning method annotated with RelayCommand")] - private async void OnLoaded() + private async Task OnLoaded() + { + // 自动处理目录配置 + await Patch1(); + + // 预热OCR + await OcrPreheating(); + + // 首次运行自动初始化绑定 + InitKeyBinding(); + + // 检查更新 + await App.GetService()!.CheckUpdateAsync(new UpdateOption()); + + // Win11下 BitBlt截图方式不可用,需要关闭窗口优化功能 + if (OsVersionHelper.IsWindows11_OrGreater && TaskContext.Instance().Config.AutoFixWin11BitBlt) + { + BitBltRegistryHelper.SetDirectXUserGlobalSettings(); + } + + // 更新仓库 + ScriptRepoUpdater.Instance.AutoUpdate(); + } + + + private void InitKeyBinding() + { + if (Config.CommonConfig.IsFirstRun) + { + try + { + var kbVm = App.GetService(); + if (kbVm != null) + { + kbVm.FetchFromRegistryCommand.Execute(null); + } + } + catch (Exception e) + { + _logger.LogError("首次运行自动初始化按键绑定异常:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); + + MessageBox.Error("读取原神键位并设置键位绑定数据时发生异常:" + e.Message + ",后续可以手动设置"); + } + + } + } + + /** + * 不同的安装目录处理 + * 可能当前目录下存在 BetterGI 的文件,需要移动到新的目录 + */ + private async Task Patch1() + { + if (Directory.Exists(Global.Absolute("BetterGI")) + // && File.Exists(Global.Absolute("BetterGI/BetterGI.exe")) + && Directory.Exists(Global.Absolute("BetterGI/User")) + ) + { + var res = await MessageBox.ShowAsync("检测到旧的 BetterGI 配置,是否迁移配置并清理旧目录?", "BetterGI", System.Windows.MessageBoxButton.YesNo, MessageBoxImage.Question); + if (res == System.Windows.MessageBoxResult.Yes) + { + var dir = Global.Absolute("BetterGI/User"); + // 迁移配置,拷贝整个目录并覆盖 + DirectoryHelper.CopyDirectory(dir, Global.Absolute("User")); + // 删除旧目录 + DirectoryHelper.DeleteDirectoryRecursively(Global.Absolute("BetterGI")); + } + + } + } + + private async Task OcrPreheating() { try { @@ -125,16 +201,5 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel { MessageBox.Warning("PaddleOcr预热失败,解决方案:https://bgi.huiyadan.com/faq.html," + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message); } - - await App.GetService()!.CheckUpdateAsync(new UpdateOption()); - - // Win11下 BitBlt截图方式不可用,需要关闭窗口优化功能 - if (OsVersionHelper.IsWindows11_OrGreater && TaskContext.Instance().Config.AutoFixWin11BitBlt) - { - BitBltRegistryHelper.SetDirectXUserGlobalSettings(); - } - - // 更新仓库 - ScriptRepoUpdater.Instance.AutoUpdate(); } -} +} \ No newline at end of file diff --git a/BetterGenshinImpact/ViewModel/Pages/KeyBindingsSettingsPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/KeyBindingsSettingsPageViewModel.cs index a35725aa..e20c8b32 100644 --- a/BetterGenshinImpact/ViewModel/Pages/KeyBindingsSettingsPageViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/KeyBindingsSettingsPageViewModel.cs @@ -372,7 +372,7 @@ public partial class KeyBindingsSettingsPageViewModel : ObservableObject, INavig { foreach (var item in keySetting) { - if (item.ElementIdentifierId is ElementIdentifierId keyId) + if (item.ElementIdentifierId is { } keyId) { // 跳过无法识别的按键 if (keyId.ToName() == "Unknown" || keyId.ToName() == "None") diff --git a/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs index c1bf7ab8..1d8b0387 100644 --- a/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs @@ -702,6 +702,7 @@ public partial class ScriptControlViewModel : ObservableObject, INavigationAware private List LoadAllJsScriptProjects() { var path = Global.ScriptPath(); + Directory.CreateDirectory(path); // 获取所有脚本项目 var projects = Directory.GetDirectories(path) .Select(x => new ScriptProject(Path.GetFileName(x)))